nx 19.8.10 → 19.8.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/nx.js CHANGED
@@ -16,6 +16,7 @@ const assert_supported_platform_1 = require("../src/native/assert-supported-plat
16
16
  const perf_hooks_1 = require("perf_hooks");
17
17
  const workspace_context_1 = require("../src/utils/workspace-context");
18
18
  const client_1 = require("../src/daemon/client/client");
19
+ const db_connection_1 = require("../src/utils/db-connection");
19
20
  function main() {
20
21
  if (process.argv[2] !== 'report' &&
21
22
  process.argv[2] !== '--version' &&
@@ -223,4 +224,11 @@ const getLatestVersionOfNx = ((fn) => {
223
224
  let cache = null;
224
225
  return () => cache || (cache = fn());
225
226
  })(_getLatestVersionOfNx);
227
+ function nxCleanup() {
228
+ (0, db_connection_1.removeDbConnections)();
229
+ }
230
+ process.on('exit', nxCleanup);
231
+ process.on('SIGINT', nxCleanup);
232
+ process.on('SIGTERM', nxCleanup);
233
+ process.on('SIGHUP', nxCleanup);
226
234
  main();
@@ -10,14 +10,14 @@ const update_manager_1 = require("../src/nx-cloud/update-manager");
10
10
  const get_cloud_options_1 = require("../src/nx-cloud/utilities/get-cloud-options");
11
11
  const nx_cloud_utils_1 = require("../src/utils/nx-cloud-utils");
12
12
  const nx_json_1 = require("../src/config/nx-json");
13
- const workspace_context_1 = require("../src/utils/workspace-context");
14
13
  const logger_1 = require("../src/utils/logger");
14
+ const workspace_context_1 = require("../src/utils/workspace-context");
15
15
  (async () => {
16
16
  const start = new Date();
17
17
  try {
18
- (0, workspace_context_1.setupWorkspaceContext)(workspace_root_1.workspaceRoot);
19
18
  if (isMainNxPackage() && (0, fileutils_1.fileExists)((0, path_1.join)(workspace_root_1.workspaceRoot, 'nx.json'))) {
20
19
  (0, assert_supported_platform_1.assertSupportedPlatform)();
20
+ (0, workspace_context_1.setupWorkspaceContext)(workspace_root_1.workspaceRoot);
21
21
  try {
22
22
  await client_1.daemonClient.stop();
23
23
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nx",
3
- "version": "19.8.10",
3
+ "version": "19.8.11",
4
4
  "private": false,
5
5
  "description": "The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.",
6
6
  "repository": {
@@ -67,7 +67,7 @@
67
67
  "yargs-parser": "21.1.1",
68
68
  "node-machine-id": "1.1.12",
69
69
  "ora": "5.3.0",
70
- "@nrwl/tao": "19.8.10"
70
+ "@nrwl/tao": "19.8.11"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "@swc-node/register": "^1.8.0",
@@ -82,16 +82,16 @@
82
82
  }
83
83
  },
84
84
  "optionalDependencies": {
85
- "@nx/nx-darwin-x64": "19.8.10",
86
- "@nx/nx-darwin-arm64": "19.8.10",
87
- "@nx/nx-linux-x64-gnu": "19.8.10",
88
- "@nx/nx-linux-x64-musl": "19.8.10",
89
- "@nx/nx-win32-x64-msvc": "19.8.10",
90
- "@nx/nx-linux-arm64-gnu": "19.8.10",
91
- "@nx/nx-linux-arm64-musl": "19.8.10",
92
- "@nx/nx-linux-arm-gnueabihf": "19.8.10",
93
- "@nx/nx-win32-arm64-msvc": "19.8.10",
94
- "@nx/nx-freebsd-x64": "19.8.10"
85
+ "@nx/nx-darwin-x64": "19.8.11",
86
+ "@nx/nx-darwin-arm64": "19.8.11",
87
+ "@nx/nx-linux-x64-gnu": "19.8.11",
88
+ "@nx/nx-linux-x64-musl": "19.8.11",
89
+ "@nx/nx-win32-x64-msvc": "19.8.11",
90
+ "@nx/nx-linux-arm64-gnu": "19.8.11",
91
+ "@nx/nx-linux-arm64-musl": "19.8.11",
92
+ "@nx/nx-linux-arm-gnueabihf": "19.8.11",
93
+ "@nx/nx-win32-arm64-msvc": "19.8.11",
94
+ "@nx/nx-freebsd-x64": "19.8.11"
95
95
  },
96
96
  "nx-migrations": {
97
97
  "migrations": "./migrations.json",
@@ -15,6 +15,7 @@ const socket_utils_1 = require("../socket-utils");
15
15
  const cache_1 = require("../cache");
16
16
  const plugins_1 = require("./plugins");
17
17
  const error_types_1 = require("../../project-graph/error-types");
18
+ const db_connection_1 = require("../../utils/db-connection");
18
19
  exports.SERVER_INACTIVITY_TIMEOUT_MS = 10800000; // 10800000 ms = 3 hours
19
20
  let watcherInstance;
20
21
  function storeWatcherInstance(instance) {
@@ -50,6 +51,7 @@ async function handleServerProcessTermination({ server, reason, sockets, }) {
50
51
  }
51
52
  (0, cache_1.deleteDaemonJsonProcessCache)();
52
53
  (0, plugins_1.cleanupPlugins)();
54
+ (0, db_connection_1.removeDbConnections)();
53
55
  logger_1.serverLogger.log(`Server stopped because: "${reason}"`);
54
56
  }
55
57
  finally {
@@ -97,6 +97,8 @@ export interface CachedResult {
97
97
  outputsPath: string
98
98
  }
99
99
 
100
+ export declare export function closeDbConnection(connection: ExternalObject<NxDbConnection>): void
101
+
100
102
  export declare export function connectToNxDb(cacheDir: string, nxVersion: string, dbName?: string | undefined | null): ExternalObject<NxDbConnection>
101
103
 
102
104
  export declare export function copy(src: string, dest: string): void
@@ -371,6 +371,7 @@ module.exports.TaskDetails = nativeBinding.TaskDetails
371
371
  module.exports.TaskHasher = nativeBinding.TaskHasher
372
372
  module.exports.Watcher = nativeBinding.Watcher
373
373
  module.exports.WorkspaceContext = nativeBinding.WorkspaceContext
374
+ module.exports.closeDbConnection = nativeBinding.closeDbConnection
374
375
  module.exports.connectToNxDb = nativeBinding.connectToNxDb
375
376
  module.exports.copy = nativeBinding.copy
376
377
  module.exports.EventType = nativeBinding.EventType
Binary file
@@ -21,9 +21,5 @@ export type CreateNodesResultWithContext = CreateNodesResult & {
21
21
  pluginName: string;
22
22
  };
23
23
  export type NormalizedPlugin = NxPluginV2 & Pick<NxPluginV1, 'processProjectGraph'>;
24
- export declare const nxPluginCache: Map<unknown, [
25
- Promise<LoadedNxPlugin>,
26
- () => void
27
- ]>;
28
24
  export declare function loadNxPlugins(plugins: PluginConfiguration[], root?: string): Promise<readonly [LoadedNxPlugin[], () => void]>;
29
25
  export declare function getDefaultPlugins(root: string): Promise<string[]>;
@@ -2,7 +2,7 @@
2
2
  // This file contains the bits and bobs of the internal API for loading and interacting with Nx plugins.
3
3
  // For the public API, used by plugin authors, see `./public-api.ts`.
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.nxPluginCache = exports.LoadedNxPlugin = void 0;
5
+ exports.LoadedNxPlugin = void 0;
6
6
  exports.loadNxPlugins = loadNxPlugins;
7
7
  exports.getDefaultPlugins = getDefaultPlugins;
8
8
  const path_1 = require("path");
@@ -66,11 +66,6 @@ class LoadedNxPlugin {
66
66
  }
67
67
  }
68
68
  exports.LoadedNxPlugin = LoadedNxPlugin;
69
- // Short lived cache (cleared between cmd runs)
70
- // holding resolved nx plugin objects.
71
- // Allows loaded plugins to not be reloaded when
72
- // referenced multiple times.
73
- exports.nxPluginCache = new Map();
74
69
  function isIsolationEnabled() {
75
70
  // Explicitly enabled, regardless of further conditions
76
71
  if (process.env.NX_ISOLATE_PLUGINS === 'true') {
@@ -106,11 +106,7 @@ export interface PluginWorkerProcessProjectGraphResult {
106
106
  tx: string;
107
107
  };
108
108
  }
109
- export interface PluginWorkerShutdownMessage {
110
- type: 'shutdown';
111
- payload: {};
112
- }
113
- export type PluginWorkerMessage = PluginWorkerLoadMessage | PluginWorkerShutdownMessage | PluginWorkerCreateNodesMessage | PluginCreateDependenciesMessage | PluginWorkerProcessProjectGraphMessage | PluginCreateMetadataMessage;
109
+ export type PluginWorkerMessage = PluginWorkerLoadMessage | PluginWorkerCreateNodesMessage | PluginCreateDependenciesMessage | PluginWorkerProcessProjectGraphMessage | PluginCreateMetadataMessage;
114
110
  export type PluginWorkerResult = PluginWorkerLoadResult | PluginWorkerCreateNodesResult | PluginCreateDependenciesResult | PluginWorkerProcessProjectGraphResult | PluginCreateMetadataResult;
115
111
  export declare function isPluginWorkerMessage(message: Serializable): message is PluginWorkerMessage;
116
112
  export declare function isPluginWorkerResult(message: Serializable): message is PluginWorkerResult;
@@ -4,12 +4,8 @@ exports.loadRemoteNxPlugin = loadRemoteNxPlugin;
4
4
  const child_process_1 = require("child_process");
5
5
  const path = require("path");
6
6
  const net_1 = require("net");
7
- // TODO (@AgentEnder): After scoped verbose logging is implemented, re-add verbose logs here.
8
- // import { logger } from '../../utils/logger';
9
- const internal_api_1 = require("../internal-api");
10
7
  const socket_utils_1 = require("../../../daemon/socket-utils");
11
8
  const consume_messages_from_socket_1 = require("../../../utils/consume-messages-from-socket");
12
- const exit_codes_1 = require("../../../utils/exit-codes");
13
9
  const messaging_1 = require("./messaging");
14
10
  const cleanupFunctions = new Set();
15
11
  const pluginNames = new Map();
@@ -29,7 +25,6 @@ async function loadRemoteNxPlugin(plugin, root) {
29
25
  const exitHandler = createWorkerExitHandler(worker, pendingPromises);
30
26
  const cleanupFunction = () => {
31
27
  worker.off('exit', exitHandler);
32
- shutdownPluginWorker(socket);
33
28
  socket.destroy();
34
29
  nxPluginWorkerCache.delete(cacheKey);
35
30
  };
@@ -55,9 +50,6 @@ async function loadRemoteNxPlugin(plugin, root) {
55
50
  nxPluginWorkerCache.set(cacheKey, pluginPromise);
56
51
  return [pluginPromise, cleanupFunction];
57
52
  }
58
- function shutdownPluginWorker(socket) {
59
- (0, messaging_1.sendMessageOverSocket)(socket, { type: 'shutdown', payload: {} });
60
- }
61
53
  /**
62
54
  * Creates a message handler for the given worker.
63
55
  * @param worker Instance of plugin-worker
@@ -195,20 +187,6 @@ function createWorkerExitHandler(worker, pendingPromises) {
195
187
  }
196
188
  };
197
189
  }
198
- let cleanedUp = false;
199
- const exitHandler = () => {
200
- internal_api_1.nxPluginCache.clear();
201
- for (const fn of cleanupFunctions) {
202
- fn();
203
- }
204
- cleanedUp = true;
205
- };
206
- process.on('exit', exitHandler);
207
- process.on('SIGINT', () => {
208
- exitHandler();
209
- process.exit((0, exit_codes_1.signalToCode)('SIGINT'));
210
- });
211
- process.on('SIGTERM', exitHandler);
212
190
  function registerPendingPromise(tx, pending, callback, context) {
213
191
  let resolver, rejector, timeout;
214
192
  const promise = new Promise((res, rej) => {
@@ -49,21 +49,6 @@ const server = (0, net_1.createServer)((socket) => {
49
49
  };
50
50
  }
51
51
  },
52
- shutdown: async () => {
53
- // Stops accepting new connections, but existing connections are
54
- // not closed immediately.
55
- server.close(() => {
56
- try {
57
- (0, fs_1.unlinkSync)(socketPath);
58
- }
59
- catch (e) { }
60
- process.exit(0);
61
- });
62
- // Closes existing connection.
63
- socket.end();
64
- // Destroys the socket once it's fully closed.
65
- socket.destroySoon();
66
- },
67
52
  createNodes: async ({ configFiles, context, tx }) => {
68
53
  try {
69
54
  const result = await plugin.createNodes[1](configFiles, context);
@@ -142,6 +127,22 @@ const server = (0, net_1.createServer)((socket) => {
142
127
  },
143
128
  });
144
129
  }));
130
+ // There should only ever be one host -> worker connection
131
+ // since the worker is spawned per host process. As such,
132
+ // we can safely close the worker when the host disconnects.
133
+ socket.on('end', () => {
134
+ // Stops accepting new connections, but existing connections are
135
+ // not closed immediately.
136
+ server.close(() => {
137
+ try {
138
+ (0, fs_1.unlinkSync)(socketPath);
139
+ }
140
+ catch (e) { }
141
+ process.exit(0);
142
+ });
143
+ // Destroys the socket once it's fully closed.
144
+ socket.destroySoon();
145
+ });
145
146
  });
146
147
  server.listen(socketPath);
147
148
  const exitHandler = (exitCode) => () => {
@@ -253,8 +253,10 @@ plugins) {
253
253
  else {
254
254
  errorBodyLines.push(` - ${e.message}`);
255
255
  }
256
- const innerStackTrace = ' ' + e.stack.split('\n').join('\n ');
257
- errorBodyLines.push(innerStackTrace);
256
+ if (e.stack) {
257
+ const innerStackTrace = ' ' + e.stack.split('\n')?.join('\n ');
258
+ errorBodyLines.push(innerStackTrace);
259
+ }
258
260
  }
259
261
  error.stack = errorBodyLines.join('\n');
260
262
  // This represents a single plugin erroring out with a hard error.
@@ -22,22 +22,19 @@ export declare class ProcessTasks {
22
22
  createTask(id: string, project: ProjectGraphProjectNode, target: string, resolvedConfiguration: string | undefined, overrides: Object): Task;
23
23
  resolveConfiguration(project: ProjectGraphProjectNode, target: string, configuration: string | undefined): string;
24
24
  getId(project: string, target: string, configuration: string | undefined): string;
25
- /**
26
- * this function is used to get the non dummy dependencies of a task recursively
27
- * For example, when we have the following dependencies:
28
- * {
29
- * 'app1:compile': [ 'app2:__nx_dummy_task__' ],
30
- * 'app2:__nx_dummy_task__': [ 'app3:__nx_dummy_task__' ],
31
- * 'app3:__nx_dummy_task__': [ 'app4:precompile' ],
32
- * 'app4:precompile': []
33
- * }
34
- * getNonDummyDeps('app1:compile') will return ['app1:compile']
35
- * getNonDummyDeps('app2:__nx_dummy_task__') will return ['app4:precompile']
36
- * getNonDummyDeps('app3:__nx_dummy_task__') will return ['app4:precompile']
37
- * getNonDummyDeps('app4:precompile') will return ['app4:precompile']
38
- */
39
- private getNonDummyDeps;
40
- private filterDummyTasks;
41
25
  }
42
26
  export declare function createTaskGraph(projectGraph: ProjectGraph, extraTargetDependencies: TargetDependencies, projectNames: string[], targets: string[], configuration: string | undefined, overrides: Object, excludeTaskDependencies?: boolean): TaskGraph;
43
27
  export declare function mapTargetDefaultsToDependencies(defaults: TargetDefaults | undefined): TargetDependencies;
28
+ /**
29
+ * This function is used to filter out the dummy tasks from the dependencies
30
+ * It will manipulate the dependencies object in place
31
+ */
32
+ export declare function filterDummyTasks(dependencies: {
33
+ [k: string]: string[];
34
+ }): void;
35
+ /**
36
+ * this function is used to get the non dummy dependencies of a task recursively
37
+ */
38
+ export declare function getNonDummyDeps(currentTask: string, dependencies: {
39
+ [k: string]: string[];
40
+ }, cycles?: Set<string>, seen?: Set<string>): string[];
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ProcessTasks = void 0;
4
4
  exports.createTaskGraph = createTaskGraph;
5
5
  exports.mapTargetDefaultsToDependencies = mapTargetDefaultsToDependencies;
6
+ exports.filterDummyTasks = filterDummyTasks;
7
+ exports.getNonDummyDeps = getNonDummyDeps;
6
8
  const utils_1 = require("./utils");
7
9
  const project_graph_utils_1 = require("../utils/project-graph-utils");
8
10
  const output_1 = require("../utils/output");
@@ -54,7 +56,7 @@ class ProcessTasks {
54
56
  this.dependencies[d] = this.dependencies[d].filter((dd) => !!initialTasks[dd]);
55
57
  }
56
58
  }
57
- this.filterDummyTasks();
59
+ filterDummyTasks(this.dependencies);
58
60
  for (const taskId of Object.keys(this.dependencies)) {
59
61
  if (this.dependencies[taskId].length > 0) {
60
62
  this.dependencies[taskId] = [
@@ -138,7 +140,7 @@ class ProcessTasks {
138
140
  }
139
141
  }
140
142
  else {
141
- const dummyId = this.getId(depProject.name, DUMMY_TASK_TARGET, undefined);
143
+ const dummyId = this.getId(depProject.name, task.target.target + DUMMY_TASK_TARGET, undefined);
142
144
  this.dependencies[task.id].push(dummyId);
143
145
  this.dependencies[dummyId] ??= [];
144
146
  const noopTask = this.createDummyTask(dummyId, task);
@@ -189,52 +191,6 @@ class ProcessTasks {
189
191
  }
190
192
  return id;
191
193
  }
192
- /**
193
- * this function is used to get the non dummy dependencies of a task recursively
194
- * For example, when we have the following dependencies:
195
- * {
196
- * 'app1:compile': [ 'app2:__nx_dummy_task__' ],
197
- * 'app2:__nx_dummy_task__': [ 'app3:__nx_dummy_task__' ],
198
- * 'app3:__nx_dummy_task__': [ 'app4:precompile' ],
199
- * 'app4:precompile': []
200
- * }
201
- * getNonDummyDeps('app1:compile') will return ['app1:compile']
202
- * getNonDummyDeps('app2:__nx_dummy_task__') will return ['app4:precompile']
203
- * getNonDummyDeps('app3:__nx_dummy_task__') will return ['app4:precompile']
204
- * getNonDummyDeps('app4:precompile') will return ['app4:precompile']
205
- */
206
- getNonDummyDeps(currentTask, originalTask, cycle) {
207
- if (currentTask === originalTask) {
208
- return [];
209
- }
210
- else if (currentTask.endsWith(DUMMY_TASK_TARGET)) {
211
- if (cycle?.length && cycle?.includes(currentTask)) {
212
- return [];
213
- }
214
- // if not a cycle, recursively get the non dummy dependencies
215
- return (this.dependencies[currentTask]?.flatMap((dep) => this.getNonDummyDeps(dep, originalTask, cycle)) ?? []);
216
- }
217
- else {
218
- return [currentTask];
219
- }
220
- }
221
- filterDummyTasks() {
222
- const cycle = (0, task_graph_utils_1.findCycle)({ dependencies: this.dependencies });
223
- for (const [key, deps] of Object.entries(this.dependencies)) {
224
- if (!key.endsWith(DUMMY_TASK_TARGET)) {
225
- const normalizedDeps = [];
226
- for (const dep of deps) {
227
- normalizedDeps.push(...this.getNonDummyDeps(dep, key, cycle));
228
- }
229
- this.dependencies[key] = normalizedDeps;
230
- }
231
- }
232
- for (const key of Object.keys(this.dependencies)) {
233
- if (key.endsWith(DUMMY_TASK_TARGET)) {
234
- delete this.dependencies[key];
235
- }
236
- }
237
- }
238
194
  }
239
195
  exports.ProcessTasks = ProcessTasks;
240
196
  function createTaskGraph(projectGraph, extraTargetDependencies, projectNames, targets, configuration, overrides, excludeTaskDependencies = false) {
@@ -268,3 +224,43 @@ function interpolateOverrides(args, projectName, project) {
268
224
  });
269
225
  return interpolatedArgs;
270
226
  }
227
+ /**
228
+ * This function is used to filter out the dummy tasks from the dependencies
229
+ * It will manipulate the dependencies object in place
230
+ */
231
+ function filterDummyTasks(dependencies) {
232
+ const cycles = (0, task_graph_utils_1.findCycles)({ dependencies });
233
+ for (const [key, deps] of Object.entries(dependencies)) {
234
+ if (!key.endsWith(DUMMY_TASK_TARGET)) {
235
+ const normalizedDeps = [];
236
+ for (const dep of deps) {
237
+ normalizedDeps.push(...getNonDummyDeps(dep, dependencies, cycles, new Set([key])));
238
+ }
239
+ dependencies[key] = normalizedDeps;
240
+ }
241
+ }
242
+ for (const key of Object.keys(dependencies)) {
243
+ if (key.endsWith(DUMMY_TASK_TARGET)) {
244
+ delete dependencies[key];
245
+ }
246
+ }
247
+ }
248
+ /**
249
+ * this function is used to get the non dummy dependencies of a task recursively
250
+ */
251
+ function getNonDummyDeps(currentTask, dependencies, cycles, seen = new Set()) {
252
+ if (seen.has(currentTask)) {
253
+ return [];
254
+ }
255
+ seen.add(currentTask);
256
+ if (currentTask.endsWith(DUMMY_TASK_TARGET)) {
257
+ if (cycles?.has(currentTask)) {
258
+ return [];
259
+ }
260
+ // if not a cycle, recursively get the non dummy dependencies
261
+ return (dependencies[currentTask]?.flatMap((dep) => getNonDummyDeps(dep, dependencies, cycles, seen)) ?? []);
262
+ }
263
+ else {
264
+ return [currentTask];
265
+ }
266
+ }
@@ -1,8 +1,19 @@
1
1
  import { ProjectGraph } from '../config/project-graph';
2
2
  import { TaskGraph } from '../config/task-graph';
3
+ /**
4
+ * This function finds a cycle in the graph.
5
+ * @returns the first cycle found, or null if no cycle is found.
6
+ */
3
7
  export declare function findCycle(graph: {
4
8
  dependencies: Record<string, string[]>;
5
9
  }): string[] | null;
10
+ /**
11
+ * This function finds all cycles in the graph.
12
+ * @returns a list of unique task ids in all cycles found, or null if no cycle is found.
13
+ */
14
+ export declare function findCycles(graph: {
15
+ dependencies: Record<string, string[]>;
16
+ }): Set<string> | null;
6
17
  export declare function makeAcyclic(graph: {
7
18
  roots: string[];
8
19
  dependencies: Record<string, string[]>;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.findCycle = findCycle;
4
+ exports.findCycles = findCycles;
4
5
  exports.makeAcyclic = makeAcyclic;
5
6
  exports.validateNoAtomizedTasks = validateNoAtomizedTasks;
6
7
  const output_1 = require("../utils/output");
@@ -17,6 +18,10 @@ function _findCycle(graph, id, visited, path) {
17
18
  }
18
19
  return null;
19
20
  }
21
+ /**
22
+ * This function finds a cycle in the graph.
23
+ * @returns the first cycle found, or null if no cycle is found.
24
+ */
20
25
  function findCycle(graph) {
21
26
  const visited = {};
22
27
  for (const t of Object.keys(graph.dependencies)) {
@@ -29,6 +34,24 @@ function findCycle(graph) {
29
34
  }
30
35
  return null;
31
36
  }
37
+ /**
38
+ * This function finds all cycles in the graph.
39
+ * @returns a list of unique task ids in all cycles found, or null if no cycle is found.
40
+ */
41
+ function findCycles(graph) {
42
+ const visited = {};
43
+ const cycles = new Set();
44
+ for (const t of Object.keys(graph.dependencies)) {
45
+ visited[t] = false;
46
+ }
47
+ for (const t of Object.keys(graph.dependencies)) {
48
+ const cycle = _findCycle(graph, t, visited, [t]);
49
+ if (cycle) {
50
+ cycle.forEach((t) => cycles.add(t));
51
+ }
52
+ }
53
+ return cycles.size ? cycles : null;
54
+ }
32
55
  function _makeAcyclic(graph, id, visited, path) {
33
56
  if (visited[id])
34
57
  return;
@@ -3,3 +3,4 @@ export declare function getDbConnection(opts?: {
3
3
  directory?: string;
4
4
  dbName?: string;
5
5
  }): ExternalObject<NxDbConnection>;
6
+ export declare function removeDbConnections(): void;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getDbConnection = getDbConnection;
4
+ exports.removeDbConnections = removeDbConnections;
4
5
  const native_1 = require("../native");
5
6
  const cache_directory_1 = require("./cache-directory");
6
7
  const package_json_1 = require("../../package.json");
@@ -11,6 +12,12 @@ function getDbConnection(opts = {}) {
11
12
  const connection = getEntryOrSet(dbConnectionMap, key, () => (0, native_1.connectToNxDb)(opts.directory, package_json_1.version, opts.dbName));
12
13
  return connection;
13
14
  }
15
+ function removeDbConnections() {
16
+ for (const connection of dbConnectionMap.values()) {
17
+ (0, native_1.closeDbConnection)(connection);
18
+ }
19
+ dbConnectionMap.clear();
20
+ }
14
21
  function getEntryOrSet(map, key, defaultValue) {
15
22
  const existing = map.get(key);
16
23
  if (existing) {