nx 18.1.0-canary.20240223-21e7648 → 18.1.0-canary.20240224-26b266f

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.
Files changed (64) hide show
  1. package/package.json +12 -12
  2. package/src/adapter/angular-json.d.ts +2 -1
  3. package/src/adapter/angular-json.js +1 -0
  4. package/src/adapter/ngcli-adapter.js +3 -3
  5. package/src/command-line/generate/generator-utils.js +2 -2
  6. package/src/command-line/init/init-v2.js +17 -11
  7. package/src/command-line/migrate/migrate.js +3 -1
  8. package/src/command-line/run/executor-utils.js +2 -2
  9. package/src/config/schema-utils.js +2 -2
  10. package/src/devkit-exports.d.ts +2 -1
  11. package/src/devkit-internals.d.ts +1 -0
  12. package/src/devkit-internals.js +3 -1
  13. package/src/generators/utils/project-configuration.js +2 -2
  14. package/src/migrations/update-15-1-0/set-project-names.js +2 -2
  15. package/src/plugins/js/index.d.ts +1 -1
  16. package/src/plugins/js/lock-file/lock-file.d.ts +1 -1
  17. package/src/plugins/js/lock-file/npm-parser.d.ts +1 -1
  18. package/src/plugins/js/lock-file/pnpm-parser.d.ts +1 -1
  19. package/src/plugins/js/lock-file/yarn-parser.d.ts +1 -1
  20. package/src/plugins/js/project-graph/build-dependencies/build-dependencies.d.ts +1 -1
  21. package/src/plugins/js/project-graph/build-dependencies/explicit-package-json-dependencies.d.ts +1 -1
  22. package/src/plugins/js/project-graph/build-dependencies/explicit-project-dependencies.d.ts +1 -1
  23. package/src/plugins/package-json-workspaces/create-nodes.d.ts +2 -2
  24. package/src/plugins/package-json-workspaces/create-nodes.js +24 -31
  25. package/src/plugins/package-json-workspaces/index.d.ts +1 -0
  26. package/src/plugins/package-json-workspaces/index.js +2 -0
  27. package/src/plugins/project-json/build-nodes/package-json-next-to-project-json.d.ts +2 -1
  28. package/src/plugins/project-json/build-nodes/package-json-next-to-project-json.js +1 -0
  29. package/src/plugins/project-json/build-nodes/project-json.d.ts +2 -1
  30. package/src/plugins/project-json/build-nodes/project-json.js +1 -0
  31. package/src/plugins/target-defaults/target-defaults-plugin.d.ts +4 -4
  32. package/src/plugins/target-defaults/target-defaults-plugin.js +24 -27
  33. package/src/project-graph/affected/locators/project-glob-changes.js +2 -3
  34. package/src/project-graph/build-project-graph.js +10 -8
  35. package/src/project-graph/file-utils.js +3 -3
  36. package/src/project-graph/plugins/index.d.ts +2 -0
  37. package/src/project-graph/plugins/index.js +8 -0
  38. package/src/project-graph/plugins/internal-api.d.ts +21 -0
  39. package/src/project-graph/plugins/internal-api.js +84 -0
  40. package/src/project-graph/plugins/messaging.d.ts +89 -0
  41. package/src/project-graph/plugins/messaging.js +23 -0
  42. package/src/project-graph/plugins/plugin-pool.d.ts +4 -0
  43. package/src/project-graph/plugins/plugin-pool.js +188 -0
  44. package/src/project-graph/plugins/plugin-worker.d.ts +1 -0
  45. package/src/project-graph/plugins/plugin-worker.js +122 -0
  46. package/src/{utils/nx-plugin.d.ts → project-graph/plugins/public-api.d.ts} +6 -40
  47. package/src/project-graph/plugins/public-api.js +4 -0
  48. package/src/project-graph/plugins/worker-api.d.ts +26 -0
  49. package/src/project-graph/plugins/worker-api.js +177 -0
  50. package/src/project-graph/project-graph-builder.d.ts +1 -1
  51. package/src/project-graph/project-graph.js +0 -2
  52. package/src/project-graph/utils/normalize-project-nodes.d.ts +1 -1
  53. package/src/project-graph/utils/project-configuration-utils.d.ts +2 -2
  54. package/src/project-graph/utils/project-configuration-utils.js +27 -47
  55. package/src/project-graph/utils/retrieve-workspace-files.d.ts +8 -4
  56. package/src/project-graph/utils/retrieve-workspace-files.js +11 -14
  57. package/src/utils/logger.d.ts +1 -0
  58. package/src/utils/logger.js +5 -0
  59. package/src/utils/nx-plugin.deprecated.d.ts +4 -2
  60. package/src/utils/nx-plugin.deprecated.js +4 -4
  61. package/src/utils/package-json.js +2 -2
  62. package/src/utils/plugins/plugin-capabilities.d.ts +1 -1
  63. package/src/utils/plugins/plugin-capabilities.js +8 -7
  64. package/src/utils/nx-plugin.js +0 -293
@@ -8,14 +8,14 @@ const assert_workspace_validity_1 = require("../utils/assert-workspace-validity"
8
8
  const nx_deps_cache_1 = require("./nx-deps-cache");
9
9
  const implicit_project_dependencies_1 = require("./utils/implicit-project-dependencies");
10
10
  const normalize_project_nodes_1 = require("./utils/normalize-project-nodes");
11
- const nx_plugin_1 = require("../utils/nx-plugin");
11
+ const internal_api_1 = require("./plugins/internal-api");
12
12
  const typescript_1 = require("../plugins/js/utils/typescript");
13
13
  const fileutils_1 = require("../utils/fileutils");
14
14
  const project_graph_builder_1 = require("./project-graph-builder");
15
15
  const configuration_1 = require("../config/configuration");
16
16
  const fs_1 = require("fs");
17
- const installation_directory_1 = require("../utils/installation-directory");
18
17
  const output_1 = require("../utils/output");
18
+ const plugin_pool_1 = require("./plugins/plugin-pool");
19
19
  let storedFileMap = null;
20
20
  let storedAllWorkspaceFiles = null;
21
21
  let storedRustReferences = null;
@@ -70,6 +70,7 @@ async function buildProjectGraphUsingProjectFileMap(projects, externalNodes, fil
70
70
  if (shouldWriteCache) {
71
71
  (0, nx_deps_cache_1.writeCache)(projectFileMapCache, projectGraph);
72
72
  }
73
+ await (0, plugin_pool_1.shutdownPluginWorkers)();
73
74
  return {
74
75
  projectGraph,
75
76
  projectFileMapCache,
@@ -140,11 +141,11 @@ function createContext(projects, nxJson, externalNodes, fileMap, filesToProcess)
140
141
  };
141
142
  }
142
143
  async function updateProjectGraphWithPlugins(context, initProjectGraph) {
143
- const plugins = await (0, nx_plugin_1.loadNxPlugins)(context.nxJsonConfiguration?.plugins, (0, installation_directory_1.getNxRequirePaths)(), context.workspaceRoot, context.projects);
144
+ const plugins = await (0, internal_api_1.loadNxPlugins)(context.nxJsonConfiguration?.plugins, context.workspaceRoot);
144
145
  let graph = initProjectGraph;
145
- for (const { plugin } of plugins) {
146
+ for (const plugin of plugins) {
146
147
  try {
147
- if ((0, nx_plugin_1.isNxPluginV1)(plugin) &&
148
+ if ((0, internal_api_1.isNxPluginV1)(plugin) &&
148
149
  plugin.processProjectGraph &&
149
150
  !plugin.createDependencies) {
150
151
  output_1.output.warn({
@@ -182,13 +183,14 @@ async function updateProjectGraphWithPlugins(context, initProjectGraph) {
182
183
  }
183
184
  }
184
185
  const builder = new project_graph_builder_1.ProjectGraphBuilder(graph, context.fileMap.projectFileMap, context.fileMap.nonProjectFiles);
185
- const createDependencyPlugins = plugins.filter(({ plugin }) => (0, nx_plugin_1.isNxPluginV2)(plugin) && plugin.createDependencies);
186
- await Promise.all(createDependencyPlugins.map(async ({ plugin, options }) => {
186
+ const createDependencyPlugins = plugins.filter((plugin) => (0, internal_api_1.isNxPluginV2)(plugin) && plugin.createDependencies);
187
+ await Promise.all(createDependencyPlugins.map(async (plugin) => {
187
188
  perf_hooks_1.performance.mark(`${plugin.name}:createDependencies - start`);
188
189
  // Set this globally to allow plugins to know if they are being called from the project graph creation
189
190
  global.NX_GRAPH_CREATION = true;
190
191
  try {
191
- const dependencies = await plugin.createDependencies(options, {
192
+ // TODO: we shouldn't have to pass null here
193
+ const dependencies = await plugin.createDependencies(null, {
192
194
  ...context,
193
195
  });
194
196
  for (const dep of dependencies) {
@@ -147,21 +147,21 @@ Object.defineProperty(exports, "workspaceLayout", { enumerable: true, get: funct
147
147
  * TODO(v19): Remove this function.
148
148
  */
149
149
  function getProjectsSyncNoInference(root, nxJson) {
150
- const projectFiles = (0, retrieve_workspace_files_1.retrieveProjectConfigurationPaths)(root, (0, nx_plugin_deprecated_1.getDefaultPluginsSync)(root));
150
+ const projectFiles = (0, retrieve_workspace_files_1.retrieveProjectConfigurationPaths)(root, (0, nx_plugin_deprecated_1.getDefaultPluginsSync)(root).map((p) => p.plugin));
151
151
  const plugins = [
152
152
  { plugin: package_json_next_to_project_json_1.PackageJsonProjectsNextToProjectJsonPlugin },
153
153
  ...(0, nx_plugin_deprecated_1.getDefaultPluginsSync)(root),
154
154
  ];
155
155
  const projectRootMap = new Map();
156
156
  // We iterate over plugins first - this ensures that plugins specified first take precedence.
157
- for (const { plugin, options } of plugins) {
157
+ for (const { plugin } of plugins) {
158
158
  const [pattern, createNodes] = plugin.createNodes ?? [];
159
159
  if (!pattern) {
160
160
  continue;
161
161
  }
162
162
  for (const file of projectFiles) {
163
163
  if ((0, minimatch_1.minimatch)(file, pattern, { dot: true })) {
164
- let r = createNodes(file, options, {
164
+ let r = createNodes(file, {}, {
165
165
  nxJsonConfiguration: nxJson,
166
166
  workspaceRoot: root,
167
167
  });
@@ -0,0 +1,2 @@
1
+ export * from './public-api';
2
+ export { readPluginPackageJson, registerPluginTSTranspiler, } from './worker-api';
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPluginTSTranspiler = exports.readPluginPackageJson = void 0;
4
+ const tslib_1 = require("tslib");
5
+ tslib_1.__exportStar(require("./public-api"), exports);
6
+ var worker_api_1 = require("./worker-api");
7
+ Object.defineProperty(exports, "readPluginPackageJson", { enumerable: true, get: function () { return worker_api_1.readPluginPackageJson; } });
8
+ Object.defineProperty(exports, "registerPluginTSTranspiler", { enumerable: true, get: function () { return worker_api_1.registerPluginTSTranspiler; } });
@@ -0,0 +1,21 @@
1
+ import { PluginConfiguration } from '../../config/nx-json';
2
+ import { NxPluginV1 } from '../../utils/nx-plugin.deprecated';
3
+ import { CreateNodesContext, CreateNodesResult, NxPlugin, NxPluginV2 } from './public-api';
4
+ export type CreateNodesResultWithContext = CreateNodesResult & {
5
+ file: string;
6
+ pluginName: string;
7
+ };
8
+ export type NormalizedPlugin = NxPluginV2 & Pick<NxPluginV1, 'processProjectGraph'>;
9
+ export type RemotePlugin = Omit<NormalizedPlugin, 'createNodes'> & {
10
+ createNodes: [
11
+ filePattern: string,
12
+ fn: (matchedFiles: string[], context: CreateNodesContext) => Promise<CreateNodesResultWithContext[]>
13
+ ];
14
+ };
15
+ export declare const nxPluginCache: Map<unknown, RemotePlugin>;
16
+ export declare function loadNxPlugins(plugins: PluginConfiguration[], root?: string): Promise<RemotePlugin[]>;
17
+ export declare function loadNxPlugin(plugin: PluginConfiguration, root?: string): Promise<RemotePlugin>;
18
+ export declare function isNxPluginV2(plugin: NxPlugin): plugin is NxPluginV2;
19
+ export declare function isNxPluginV1(plugin: NxPlugin | RemotePlugin): plugin is NxPluginV1;
20
+ export declare function normalizeNxPlugin(plugin: NxPlugin): NormalizedPlugin;
21
+ export declare function getDefaultPlugins(root: string): Promise<string[]>;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ // This file contains the bits and bobs of the internal API for loading and interacting with Nx plugins.
3
+ // For the public API, used by plugin authors, see `./public-api.ts`.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.getDefaultPlugins = exports.normalizeNxPlugin = exports.isNxPluginV1 = exports.isNxPluginV2 = exports.loadNxPlugin = exports.loadNxPlugins = exports.nxPluginCache = void 0;
6
+ const path_1 = require("path");
7
+ const workspaces_1 = require("../../config/workspaces");
8
+ const globs_1 = require("../../utils/globs");
9
+ const workspace_root_1 = require("../../utils/workspace-root");
10
+ const angular_json_1 = require("../../adapter/angular-json");
11
+ const plugin_pool_1 = require("./plugin-pool");
12
+ // Short lived cache (cleared between cmd runs)
13
+ // holding resolved nx plugin objects.
14
+ // Allows loaded plugins to not be reloaded when
15
+ // referenced multiple times.
16
+ exports.nxPluginCache = new Map();
17
+ async function loadNxPlugins(plugins, root = workspace_root_1.workspaceRoot) {
18
+ const result = [];
19
+ plugins ??= [];
20
+ plugins.unshift((0, path_1.join)(__dirname, '../../plugins/project-json/build-nodes/package-json-next-to-project-json'));
21
+ // We push the nx core node plugins onto the end, s.t. it overwrites any other plugins
22
+ plugins.push(...(await getDefaultPlugins(root)));
23
+ for (const plugin of plugins) {
24
+ result.push(loadNxPlugin(plugin, root));
25
+ }
26
+ return Promise.all(result);
27
+ }
28
+ exports.loadNxPlugins = loadNxPlugins;
29
+ async function loadNxPlugin(plugin, root = workspace_root_1.workspaceRoot) {
30
+ const cacheKey = JSON.stringify(plugin);
31
+ if (exports.nxPluginCache.has(cacheKey)) {
32
+ return exports.nxPluginCache.get(cacheKey);
33
+ }
34
+ const loadedPlugin = await (0, plugin_pool_1.loadRemoteNxPlugin)(plugin, root);
35
+ exports.nxPluginCache.set(cacheKey, loadedPlugin);
36
+ return loadedPlugin;
37
+ }
38
+ exports.loadNxPlugin = loadNxPlugin;
39
+ function isNxPluginV2(plugin) {
40
+ return 'createNodes' in plugin || 'createDependencies' in plugin;
41
+ }
42
+ exports.isNxPluginV2 = isNxPluginV2;
43
+ function isNxPluginV1(plugin) {
44
+ return 'processProjectGraph' in plugin || 'projectFilePatterns' in plugin;
45
+ }
46
+ exports.isNxPluginV1 = isNxPluginV1;
47
+ function normalizeNxPlugin(plugin) {
48
+ if (isNxPluginV2(plugin)) {
49
+ return plugin;
50
+ }
51
+ if (isNxPluginV1(plugin) && plugin.projectFilePatterns) {
52
+ return {
53
+ ...plugin,
54
+ createNodes: [
55
+ `*/**/${(0, globs_1.combineGlobPatterns)(plugin.projectFilePatterns)}`,
56
+ (configFilePath) => {
57
+ const root = (0, path_1.dirname)(configFilePath);
58
+ return {
59
+ projects: {
60
+ [root]: {
61
+ name: (0, workspaces_1.toProjectName)(configFilePath),
62
+ targets: plugin.registerProjectTargets?.(configFilePath),
63
+ },
64
+ },
65
+ };
66
+ },
67
+ ],
68
+ };
69
+ }
70
+ return plugin;
71
+ }
72
+ exports.normalizeNxPlugin = normalizeNxPlugin;
73
+ async function getDefaultPlugins(root) {
74
+ return [
75
+ (0, path_1.join)(__dirname, '../../plugins/js'),
76
+ (0, path_1.join)(__dirname, '../../plugins/target-defaults/target-defaults-plugin'),
77
+ ...((0, angular_json_1.shouldMergeAngularProjects)(root, false)
78
+ ? [(0, path_1.join)(__dirname, '../../adapter/angular-json')]
79
+ : []),
80
+ (0, path_1.join)(__dirname, '../../plugins/package-json-workspaces'),
81
+ (0, path_1.join)(__dirname, '../../plugins/project-json/build-nodes/project-json'),
82
+ ];
83
+ }
84
+ exports.getDefaultPlugins = getDefaultPlugins;
@@ -0,0 +1,89 @@
1
+ import { ProjectGraph, ProjectGraphProcessorContext } from '../../config/project-graph';
2
+ import { PluginConfiguration } from '../../config/nx-json';
3
+ import { CreateDependenciesContext, CreateNodesContext } from './public-api';
4
+ import { RemotePlugin } from './internal-api';
5
+ export interface PluginWorkerLoadMessage {
6
+ type: 'load';
7
+ payload: {
8
+ plugin: PluginConfiguration;
9
+ root: string;
10
+ };
11
+ }
12
+ export interface PluginWorkerLoadResult {
13
+ type: 'load-result';
14
+ payload: {
15
+ name: string;
16
+ createNodesPattern: string;
17
+ hasCreateDependencies: boolean;
18
+ hasProcessProjectGraph: boolean;
19
+ success: true;
20
+ } | {
21
+ success: false;
22
+ error: string;
23
+ };
24
+ }
25
+ export interface PluginWorkerShutdownMessage {
26
+ type: 'shutdown';
27
+ payload: undefined;
28
+ }
29
+ export interface PluginWorkerCreateNodesMessage {
30
+ type: 'createNodes';
31
+ payload: {
32
+ configFiles: string[];
33
+ context: CreateNodesContext;
34
+ };
35
+ }
36
+ export interface PluginWorkerCreateNodesResult {
37
+ type: 'createNodesResult';
38
+ payload: {
39
+ success: true;
40
+ result: Awaited<ReturnType<RemotePlugin['createNodes'][1]>>;
41
+ } | {
42
+ success: false;
43
+ error: string;
44
+ };
45
+ }
46
+ export interface PluginCreateDependenciesMessage {
47
+ type: 'createDependencies';
48
+ payload: {
49
+ context: CreateDependenciesContext;
50
+ };
51
+ }
52
+ export interface PluginCreateDependenciesResult {
53
+ type: 'createDependenciesResult';
54
+ payload: {
55
+ dependencies: ReturnType<RemotePlugin['createDependencies']>;
56
+ success: true;
57
+ } | {
58
+ success: false;
59
+ error: string;
60
+ };
61
+ }
62
+ export interface PluginWorkerProcessProjectGraphMessage {
63
+ type: 'processProjectGraph';
64
+ payload: {
65
+ graph: ProjectGraph;
66
+ ctx: ProjectGraphProcessorContext;
67
+ };
68
+ }
69
+ export interface PluginWorkerProcessProjectGraphResult {
70
+ type: 'processProjectGraphResult';
71
+ payload: {
72
+ graph: ProjectGraph;
73
+ success: true;
74
+ } | {
75
+ success: false;
76
+ error: string;
77
+ };
78
+ }
79
+ export type PluginWorkerMessage = PluginWorkerLoadMessage | PluginWorkerShutdownMessage | PluginWorkerCreateNodesMessage | PluginCreateDependenciesMessage | PluginWorkerProcessProjectGraphMessage;
80
+ export type PluginWorkerResult = PluginWorkerLoadResult | PluginWorkerCreateNodesResult | PluginCreateDependenciesResult | PluginWorkerProcessProjectGraphResult;
81
+ type MaybePromise<T> = T | Promise<T>;
82
+ type MessageHandlerReturn<T extends PluginWorkerMessage | PluginWorkerResult> = T extends PluginWorkerResult ? MaybePromise<PluginWorkerMessage | void> : MaybePromise<PluginWorkerResult | void>;
83
+ export declare function consumeMessage<T extends PluginWorkerMessage | PluginWorkerResult>(raw: string | T, handlers: {
84
+ [K in T['type']]: (payload: Extract<T, {
85
+ type: K;
86
+ }>['payload']) => MessageHandlerReturn<T>;
87
+ }): Promise<void>;
88
+ export declare function createMessage(message: PluginWorkerMessage | PluginWorkerResult): string;
89
+ export {};
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMessage = exports.consumeMessage = void 0;
4
+ // Takes a message and a map of handlers and calls the appropriate handler
5
+ // type safe and requires all handlers to be handled
6
+ async function consumeMessage(raw, handlers) {
7
+ const message = typeof raw === 'string' ? JSON.parse(raw) : raw;
8
+ const handler = handlers[message.type];
9
+ if (handler) {
10
+ const response = await handler(message.payload);
11
+ if (response) {
12
+ process.send(createMessage(response));
13
+ }
14
+ }
15
+ else {
16
+ throw new Error(`Unhandled message type: ${message.type}`);
17
+ }
18
+ }
19
+ exports.consumeMessage = consumeMessage;
20
+ function createMessage(message) {
21
+ return JSON.stringify(message);
22
+ }
23
+ exports.createMessage = createMessage;
@@ -0,0 +1,4 @@
1
+ import { PluginConfiguration } from '../../config/nx-json';
2
+ import { RemotePlugin } from './internal-api';
3
+ export declare function loadRemoteNxPlugin(plugin: PluginConfiguration, root: string): Promise<RemotePlugin>;
4
+ export declare function shutdownPluginWorkers(): Promise<any[]>;
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shutdownPluginWorkers = exports.loadRemoteNxPlugin = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const path = require("path");
6
+ const logger_1 = require("../../utils/logger");
7
+ const internal_api_1 = require("./internal-api");
8
+ const messaging_1 = require("./messaging");
9
+ const pool = [];
10
+ const pidMap = new Map();
11
+ function loadRemoteNxPlugin(plugin, root) {
12
+ // this should only really be true when running unit tests within
13
+ // the Nx repo. We still need to start the worker in this case,
14
+ // but its typescript.
15
+ const isWorkerTypescript = path.extname(__filename) === '.ts';
16
+ const workerPath = path.join(__dirname, 'plugin-worker');
17
+ const worker = (0, child_process_1.fork)(workerPath, [], {
18
+ stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
19
+ env: {
20
+ ...process.env,
21
+ ...(isWorkerTypescript
22
+ ? {
23
+ // Ensures that the worker uses the same tsconfig as the main process
24
+ TS_NODE_PROJECT: path.join(__dirname, '../../../tsconfig.lib.json'),
25
+ }
26
+ : {}),
27
+ },
28
+ execArgv: [
29
+ ...process.execArgv,
30
+ // If the worker is typescript, we need to register ts-node
31
+ ...(isWorkerTypescript ? ['-r', 'ts-node/register'] : []),
32
+ ],
33
+ });
34
+ worker.send((0, messaging_1.createMessage)({ type: 'load', payload: { plugin, root } }));
35
+ pool.push(worker);
36
+ logger_1.logger.verbose(`[plugin-worker] started worker: ${worker.pid}`);
37
+ return new Promise((res, rej) => {
38
+ worker.on('message', createWorkerHandler(worker, res, rej));
39
+ worker.on('exit', () => workerOnExitHandler(worker));
40
+ });
41
+ }
42
+ exports.loadRemoteNxPlugin = loadRemoteNxPlugin;
43
+ let pluginWorkersShutdown = false;
44
+ async function shutdownPluginWorkers() {
45
+ // Clears the plugin cache so no refs to the workers are held
46
+ internal_api_1.nxPluginCache.clear();
47
+ // Marks the workers as shutdown so that we don't report unexpected exits
48
+ pluginWorkersShutdown = true;
49
+ const promises = [];
50
+ for (const p of pool) {
51
+ p.send((0, messaging_1.createMessage)({ type: 'shutdown', payload: undefined }), (error) => {
52
+ if (error) {
53
+ // This occurs when the worker is already dead, and we can ignore it
54
+ }
55
+ else {
56
+ promises.push(
57
+ // Create a promise that resolves when the worker exits
58
+ new Promise((res, rej) => {
59
+ p.once('exit', () => res());
60
+ }));
61
+ }
62
+ });
63
+ }
64
+ return Promise.all(promises);
65
+ }
66
+ exports.shutdownPluginWorkers = shutdownPluginWorkers;
67
+ /**
68
+ * Creates a message handler for the given worker.
69
+ * @param worker Instance of plugin-worker
70
+ * @param onload Resolver for RemotePlugin promise
71
+ * @param onloadError Rejecter for RemotePlugin promise
72
+ * @returns Function to handle messages from the worker
73
+ */
74
+ function createWorkerHandler(worker, onload, onloadError) {
75
+ // We store resolver and rejecter functions in the outer scope so that we can
76
+ // resolve/reject the promise from the message handler. The flow is something like:
77
+ // 1. plugin api called
78
+ // 2. remote plugin sends message to worker, creates promise and stores resolver/rejecter
79
+ // 3. worker performs API request
80
+ // 4. worker sends result back to main process
81
+ // 5. main process resolves/rejects promise based on result
82
+ let createNodesResolver;
83
+ let createNodesRejecter;
84
+ let createDependenciesResolver;
85
+ let createDependenciesRejecter;
86
+ let processProjectGraphResolver;
87
+ let processProjectGraphRejecter;
88
+ let pluginName;
89
+ return function (message) {
90
+ const parsed = JSON.parse(message);
91
+ logger_1.logger.verbose(`[plugin-pool] received message: ${parsed.type} from ${pluginName ?? worker.pid}`);
92
+ (0, messaging_1.consumeMessage)(parsed, {
93
+ 'load-result': (result) => {
94
+ if (result.success) {
95
+ const { name, createNodesPattern } = result;
96
+ pluginName = name;
97
+ pidMap.set(worker.pid, name);
98
+ onload({
99
+ name,
100
+ createNodes: createNodesPattern
101
+ ? [
102
+ createNodesPattern,
103
+ (configFiles, ctx) => {
104
+ return new Promise((res, rej) => {
105
+ worker.send((0, messaging_1.createMessage)({
106
+ type: 'createNodes',
107
+ payload: { configFiles, context: ctx },
108
+ }));
109
+ createNodesResolver = res;
110
+ createNodesRejecter = rej;
111
+ });
112
+ },
113
+ ]
114
+ : undefined,
115
+ createDependencies: result.hasCreateDependencies
116
+ ? (opts, ctx) => {
117
+ return new Promise((res, rej) => {
118
+ worker.send((0, messaging_1.createMessage)({
119
+ type: 'createDependencies',
120
+ payload: { context: ctx },
121
+ }));
122
+ createDependenciesResolver = res;
123
+ createDependenciesRejecter = rej;
124
+ });
125
+ }
126
+ : undefined,
127
+ processProjectGraph: result.hasProcessProjectGraph
128
+ ? (graph, ctx) => {
129
+ return new Promise((res, rej) => {
130
+ worker.send((0, messaging_1.createMessage)({
131
+ type: 'processProjectGraph',
132
+ payload: { graph, ctx },
133
+ }));
134
+ processProjectGraphResolver = res;
135
+ processProjectGraphRejecter = rej;
136
+ });
137
+ }
138
+ : undefined,
139
+ });
140
+ }
141
+ else if (result.success === false) {
142
+ onloadError(result.error);
143
+ }
144
+ },
145
+ createDependenciesResult: (result) => {
146
+ if (result.success) {
147
+ createDependenciesResolver(result.dependencies);
148
+ createDependenciesResolver = undefined;
149
+ }
150
+ else if (result.success === false) {
151
+ createDependenciesRejecter(result.error);
152
+ createDependenciesRejecter = undefined;
153
+ }
154
+ },
155
+ createNodesResult: (payload) => {
156
+ if (payload.success) {
157
+ createNodesResolver(payload.result);
158
+ createNodesResolver = undefined;
159
+ }
160
+ else if (payload.success === false) {
161
+ createNodesRejecter(payload.error);
162
+ createNodesRejecter = undefined;
163
+ }
164
+ },
165
+ processProjectGraphResult: (result) => {
166
+ if (result.success) {
167
+ processProjectGraphResolver(result.graph);
168
+ processProjectGraphResolver = undefined;
169
+ }
170
+ else if (result.success === false) {
171
+ processProjectGraphRejecter(result.error);
172
+ processProjectGraphRejecter = undefined;
173
+ }
174
+ },
175
+ });
176
+ };
177
+ }
178
+ function workerOnExitHandler(worker) {
179
+ return () => {
180
+ if (!pluginWorkersShutdown) {
181
+ shutdownPluginWorkers();
182
+ throw new Error(`[Nx] plugin worker ${pidMap.get(worker.pid) ?? worker.pid} exited unexpectedly`);
183
+ }
184
+ };
185
+ }
186
+ process.on('exit', () => {
187
+ shutdownPluginWorkers();
188
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const installation_directory_1 = require("../../utils/installation-directory");
4
+ const worker_api_1 = require("./worker-api");
5
+ const messaging_1 = require("./messaging");
6
+ const retrieve_workspace_files_1 = require("../utils/retrieve-workspace-files");
7
+ global.NX_GRAPH_CREATION = true;
8
+ let plugin;
9
+ let pluginOptions;
10
+ process.on('message', async (message) => {
11
+ (0, messaging_1.consumeMessage)(message, {
12
+ load: async ({ plugin: pluginConfiguration, root }) => {
13
+ process.chdir(root);
14
+ try {
15
+ ({ plugin, options: pluginOptions } = await loadPluginFromWorker(pluginConfiguration, root));
16
+ return {
17
+ type: 'load-result',
18
+ payload: {
19
+ name: plugin.name,
20
+ createNodesPattern: plugin.createNodes?.[0],
21
+ hasCreateDependencies: 'createDependencies' in plugin && !!plugin.createDependencies,
22
+ hasProcessProjectGraph: 'processProjectGraph' in plugin && !!plugin.processProjectGraph,
23
+ success: true,
24
+ },
25
+ };
26
+ }
27
+ catch (e) {
28
+ return {
29
+ type: 'load-result',
30
+ payload: {
31
+ success: false,
32
+ error: `Could not load plugin ${plugin} \n ${e instanceof Error ? e.stack : ''}`,
33
+ },
34
+ };
35
+ }
36
+ },
37
+ shutdown: async () => {
38
+ process.exit(0);
39
+ },
40
+ createNodes: async ({ configFiles, context }) => {
41
+ try {
42
+ const result = await runCreateNodesInParallel(configFiles, context);
43
+ return {
44
+ type: 'createNodesResult',
45
+ payload: { result, success: true },
46
+ };
47
+ }
48
+ catch (e) {
49
+ return {
50
+ type: 'createNodesResult',
51
+ payload: { success: false, error: e.stack },
52
+ };
53
+ }
54
+ },
55
+ createDependencies: async (payload) => {
56
+ try {
57
+ const result = await plugin.createDependencies(pluginOptions, payload.context);
58
+ return {
59
+ type: 'createDependenciesResult',
60
+ payload: { dependencies: result, success: true },
61
+ };
62
+ }
63
+ catch (e) {
64
+ return {
65
+ type: 'createDependenciesResult',
66
+ payload: { success: false, error: e.stack },
67
+ };
68
+ }
69
+ },
70
+ processProjectGraph: async ({ graph, ctx }) => {
71
+ try {
72
+ const result = await plugin.processProjectGraph(graph, ctx);
73
+ return {
74
+ type: 'processProjectGraphResult',
75
+ payload: { graph: result, success: true },
76
+ };
77
+ }
78
+ catch (e) {
79
+ return {
80
+ type: 'processProjectGraphResult',
81
+ payload: { success: false, error: e.stack },
82
+ };
83
+ }
84
+ },
85
+ });
86
+ });
87
+ let projectsWithoutInference;
88
+ async function loadPluginFromWorker(plugin, root) {
89
+ try {
90
+ require.resolve(typeof plugin === 'string' ? plugin : plugin.plugin);
91
+ }
92
+ catch {
93
+ // If a plugin cannot be resolved, we will need projects to resolve it
94
+ projectsWithoutInference ??=
95
+ await (0, retrieve_workspace_files_1.retrieveProjectConfigurationsWithoutPluginInference)(root);
96
+ }
97
+ return await (0, worker_api_1.loadNxPluginAsync)(plugin, (0, installation_directory_1.getNxRequirePaths)(root), projectsWithoutInference, root);
98
+ }
99
+ function runCreateNodesInParallel(configFiles, context) {
100
+ const promises = configFiles.map((file) => {
101
+ performance.mark(`${plugin.name}:createNodes:${file} - start`);
102
+ const value = plugin.createNodes[1](file, pluginOptions, context);
103
+ if (value instanceof Promise) {
104
+ return value
105
+ .catch((e) => {
106
+ performance.mark(`${plugin.name}:createNodes:${file} - end`);
107
+ throw new Error(`Unable to create nodes for ${file} using plugin ${plugin.name}.`, e);
108
+ })
109
+ .then((r) => {
110
+ performance.mark(`${plugin.name}:createNodes:${file} - end`);
111
+ performance.measure(`${plugin.name}:createNodes:${file}`, `${plugin.name}:createNodes:${file} - start`, `${plugin.name}:createNodes:${file} - end`);
112
+ return { ...r, pluginName: plugin.name, file };
113
+ });
114
+ }
115
+ else {
116
+ performance.mark(`${plugin.name}:createNodes:${file} - end`);
117
+ performance.measure(`${plugin.name}:createNodes:${file}`, `${plugin.name}:createNodes:${file} - start`, `${plugin.name}:createNodes:${file} - end`);
118
+ return { ...value, pluginName: plugin.name, file };
119
+ }
120
+ });
121
+ return Promise.all(promises);
122
+ }