feat(core): load native files from tmp location instead of node_modules (#22648)

This commit is contained in:
MaxKless 2024-04-17 17:14:28 +02:00 committed by GitHub
parent 45d89d21d4
commit da1808d36c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 508 additions and 377 deletions

View File

@ -17,6 +17,10 @@
{ {
"group": ["nx/src/plugins/js*"], "group": ["nx/src/plugins/js*"],
"message": "Imports from 'nx/src/plugins/js' are not allowed. Use '@nx/js' instead" "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."
} }
] ]
} }

View File

@ -25,6 +25,10 @@
{ {
"group": ["nx/*"], "group": ["nx/*"],
"message": "Circular import in 'nx' found. Use relative path." "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."
} }
] ]
} }

View File

@ -26,7 +26,8 @@ function main() {
if ( if (
process.argv[2] !== 'report' && process.argv[2] !== 'report' &&
process.argv[2] !== '--version' && process.argv[2] !== '--version' &&
process.argv[2] !== '--help' process.argv[2] !== '--help' &&
process.argv[2] !== 'reset'
) { ) {
assertSupportedPlatform(); assertSupportedPlatform();
} }

View File

@ -10,7 +10,7 @@
"executor": "@monodon/rust:napi", "executor": "@monodon/rust:napi",
"options": { "options": {
"dist": "packages/nx/src/native", "dist": "packages/nx/src/native",
"jsFile": "packages/nx/src/native/index.js", "jsFile": "packages/nx/src/native/native-bindings.js",
"release": true "release": true
}, },
"configurations": { "configurations": {

View File

@ -1,12 +1,17 @@
import { execSync } from 'child_process'; import { execSync } from 'child_process';
export function checkForUncommittedChanges() { export function checkForUncommittedChanges() {
const gitResult = execSync(`git status --porcelain`); const gitResult = execSync('git status --porcelain').toString();
if (gitResult.length > 0) {
const filteredResults = gitResult
.split('\n')
.filter((line) => !line.includes('.nx') && line.trim().length > 0);
if (filteredResults.length > 0) {
console.log('❗️ Careful!'); console.log('❗️ Careful!');
console.log('You have uncommitted changes in your repository.'); console.log('You have uncommitted changes in your repository.');
console.log(''); console.log('');
console.log(gitResult.toString()); console.log(filteredResults.join('\n').toString());
console.log('Please commit your changes before running the migrator!'); console.log('Please commit your changes before running the migrator!');
process.exit(1); process.exit(1);
} }

View File

@ -43,6 +43,25 @@ export async function safelyCleanUpExistingProcess(): Promise<void> {
if (daemonProcessJson && daemonProcessJson.processId) { if (daemonProcessJson && daemonProcessJson.processId) {
try { try {
process.kill(daemonProcessJson.processId); process.kill(daemonProcessJson.processId);
// we wait for the process to actually shut down before returning
await new Promise<void>((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 {} } catch {}
} }
deleteDaemonJsonProcessCache(); deleteDaemonJsonProcessCache();

View File

@ -25,8 +25,10 @@ import { safelyCleanUpExistingProcess } from '../cache';
import { Hash } from '../../hasher/task-hasher'; import { Hash } from '../../hasher/task-hasher';
import { Task, TaskGraph } from '../../config/task-graph'; import { Task, TaskGraph } from '../../config/task-graph';
import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration-utils'; import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration-utils';
import { DaemonProjectGraphError } from '../daemon-project-graph-error'; import {
import { ProjectGraphError } from '../../project-graph/project-graph'; DaemonProjectGraphError,
ProjectGraphError,
} from '../../project-graph/error-types';
const DAEMON_ENV_SETTINGS = { const DAEMON_ENV_SETTINGS = {
NX_PROJECT_GLOB_CACHE: 'false', NX_PROJECT_GLOB_CACHE: 'false',

View File

@ -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;
}
}

View File

@ -2,7 +2,7 @@ import { Task, TaskGraph } from '../../config/task-graph';
import { getCachedSerializedProjectGraphPromise } from './project-graph-incremental-recomputation'; import { getCachedSerializedProjectGraphPromise } from './project-graph-incremental-recomputation';
import { InProcessTaskHasher } from '../../hasher/task-hasher'; import { InProcessTaskHasher } from '../../hasher/task-hasher';
import { readNxJson } from '../../config/configuration'; 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 * We use this not to recreated hasher for every hash operation

View File

@ -33,10 +33,12 @@ import { notifyFileWatcherSockets } from './file-watching/file-watcher-sockets';
import { serverLogger } from './logger'; import { serverLogger } from './logger';
import { NxWorkspaceFilesExternals } from '../../native'; import { NxWorkspaceFilesExternals } from '../../native';
import { ConfigurationResult } from '../../project-graph/utils/project-configuration-utils'; 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 { LoadedNxPlugin } from '../../project-graph/plugins/internal-api';
import { getPlugins } from './plugins'; import { getPlugins } from './plugins';
import { ProjectConfigurationsError } from '../../project-graph/error-types'; import {
DaemonProjectGraphError,
ProjectConfigurationsError,
} from '../../project-graph/error-types';
interface SerializedProjectGraph { interface SerializedProjectGraph {
error: Error | null; error: Error | null;

View File

@ -2,7 +2,7 @@ import { unlinkSync } from 'fs';
import { platform } from 'os'; import { platform } from 'os';
import { join, resolve } from 'path'; import { join, resolve } from 'path';
import { DAEMON_SOCKET_PATH, socketDir } from './tmp-dir'; 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'; export const isWindows = platform() === 'win32';

View File

@ -1,268 +1,77 @@
const { existsSync, readFileSync } = require('fs') const { join, basename } = require('path');
const { join } = 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 const localNodeFiles = [
let localFileExisted = false 'nx.android-arm64.node',
let loadError = null '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() { const originalLoad = Module._load;
// For Node 10
if (!process.report || typeof process.report.getReport !== 'function') { // We override the _load function so that when a native file is required,
try { // we copy it to a cache directory and require it from there.
const lddPath = require('child_process').execSync('which ldd').toString().trim(); // This prevents the file being loaded from node_modules and causing file locking issues.
return readFileSync(lddPath, 'utf8').includes('musl') // Will only be called once because the require cache takes over afterwards.
} catch (e) { Module._load = function (request, parent, isMain) {
return true 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]);
} }
if (!existsSync(cacheDir)) {
mkdirSync(cacheDir, { recursive: true });
}
copyFileSync(nativeLocation, tmpFile);
return originalLoad.apply(this, [tmpFile, parent, isMain]);
} else { } else {
const { glibcVersionRuntime } = process.report.getReport().header // call the original _load function for everything else
return !glibcVersionRuntime return originalLoad.apply(this, arguments);
} }
} };
switch (platform) { const indexModulePath = require.resolve('./native-bindings.js');
case 'android': delete require.cache[indexModulePath];
switch (arch) { const indexModule = require('./native-bindings.js');
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 = indexModule;
Module._load = originalLoad;
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

View File

@ -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;

View File

@ -1,6 +1,75 @@
import { CreateNodesResultWithContext } from './plugins/internal-api'; 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 { 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 { export class ProjectsWithConflictingNamesError extends Error {
constructor( constructor(
@ -153,3 +222,16 @@ export function isMergeNodesError(e: unknown): e is MergeNodesError {
(typeof e === 'object' && 'name' in e && e?.name === MergeNodesError.name) (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;
}
}

View File

@ -1,44 +1,33 @@
import { import { performance } from 'perf_hooks';
readFileMapCache, import { readNxJson } from '../config/nx-json';
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 { ProjectGraph } from '../config/project-graph'; import { ProjectGraph } from '../config/project-graph';
import { stripIndents } from '../utils/strip-indents';
import { import {
ProjectConfiguration, ProjectConfiguration,
ProjectsConfigurations, ProjectsConfigurations,
} from '../config/workspace-json-project-json'; } from '../config/workspace-json-project-json';
import { daemonClient } from '../daemon/client/client'; import { daemonClient } from '../daemon/client/client';
import { markDaemonAsDisabled, writeDaemonLogs } from '../daemon/tmp-dir';
import { fileExists } from '../utils/fileutils'; import { fileExists } from '../utils/fileutils';
import { output } from '../utils/output';
import { stripIndents } from '../utils/strip-indents';
import { workspaceRoot } from '../utils/workspace-root'; 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 { import {
retrieveProjectConfigurations, retrieveProjectConfigurations,
retrieveWorkspaceFiles, retrieveWorkspaceFiles,
} from './utils/retrieve-workspace-files'; } 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. * 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) { function handleProjectGraphError(opts: { exitOnError: boolean }, e) {
if (opts.exitOnError) { if (opts.exitOnError) {
const isVerbose = process.env.NX_VERBOSE_LOGGING === 'true'; const isVerbose = process.env.NX_VERBOSE_LOGGING === 'true';

View File

@ -5,7 +5,7 @@ import type {
ProjectsConfigurations, ProjectsConfigurations,
} from '../config/workspace-json-project-json'; } from '../config/workspace-json-project-json';
import { output } from './output'; 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; const LIST_CHOICE_DISPLAY_LIMIT = 10;