fix(core): avoid launching default plugins twice (#29539)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior Default plugins are launched twice when loading plugins in a workspace that has local plugins: - Once to resolve the local plugin - Once to be used as an actual plugin ## Expected Behavior Default plugins are launched once and reused ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes # --------- Co-authored-by: FrozenPandaz <jasonjean1993@gmail.com>
This commit is contained in:
parent
fb318005f2
commit
0edd1102f7
@ -7,7 +7,7 @@ import {
|
||||
parseTargetString,
|
||||
} from '@nx/devkit';
|
||||
import { addE2eCiTargetDefaults as _addE2eCiTargetDefaults } from '@nx/devkit/src/generators/target-defaults-utils';
|
||||
import { LoadedNxPlugin } from 'nx/src/project-graph/plugins/internal-api';
|
||||
import { LoadedNxPlugin } from 'nx/src/project-graph/plugins/loaded-nx-plugin';
|
||||
import type { ConfigurationResult } from 'nx/src/project-graph/utils/project-configuration-utils';
|
||||
import {
|
||||
ProjectConfigurationsError,
|
||||
|
||||
@ -3,7 +3,10 @@ import { TempFs } from '../internal-testing-utils/temp-fs';
|
||||
import { withEnvironmentVariables } from '../internal-testing-utils/with-environment';
|
||||
import { retrieveProjectConfigurations } from '../project-graph/utils/retrieve-workspace-files';
|
||||
import { readNxJson } from './configuration';
|
||||
import { loadNxPlugins } from '../project-graph/plugins/internal-api';
|
||||
import {
|
||||
cleanupPlugins,
|
||||
getPlugins,
|
||||
} from '../project-graph/plugins/get-plugins';
|
||||
|
||||
describe('Workspaces', () => {
|
||||
let fs: TempFs;
|
||||
@ -40,16 +43,13 @@ describe('Workspaces', () => {
|
||||
NX_WORKSPACE_ROOT_PATH: fs.tempDir,
|
||||
},
|
||||
async () => {
|
||||
const [plugins, cleanup] = await loadNxPlugins(
|
||||
readNxJson(fs.tempDir).plugins,
|
||||
fs.tempDir
|
||||
);
|
||||
const plugins = await getPlugins(fs.tempDir);
|
||||
const res = await retrieveProjectConfigurations(
|
||||
plugins,
|
||||
fs.tempDir,
|
||||
readNxJson(fs.tempDir)
|
||||
);
|
||||
cleanup();
|
||||
cleanupPlugins();
|
||||
return res;
|
||||
}
|
||||
);
|
||||
|
||||
@ -30,7 +30,7 @@ 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 { LoadedNxPlugin } from '../../project-graph/plugins/internal-api';
|
||||
import type { LoadedNxPlugin } from '../../project-graph/plugins/loaded-nx-plugin';
|
||||
import {
|
||||
DaemonProjectGraphError,
|
||||
ProjectConfigurationsError,
|
||||
|
||||
@ -26,7 +26,7 @@ export {
|
||||
findProjectForPath,
|
||||
} from './project-graph/utils/find-project-for-path';
|
||||
export { retrieveProjectConfigurations } from './project-graph/utils/retrieve-workspace-files';
|
||||
export { LoadedNxPlugin } from './project-graph/plugins/internal-api';
|
||||
export { LoadedNxPlugin } from './project-graph/plugins/loaded-nx-plugin';
|
||||
export * from './project-graph/error-types';
|
||||
export { registerTsProject } from './plugins/js/utils/register';
|
||||
export { interpolate } from './tasks-runner/utils';
|
||||
|
||||
@ -5,7 +5,6 @@ const tempFs = new TempFs('explicit-project-deps');
|
||||
import { ProjectGraphProjectNode } from '../../../../config/project-graph';
|
||||
import { ProjectConfiguration } from '../../../../config/workspace-json-project-json';
|
||||
import { CreateDependenciesContext } from '../../../../project-graph/plugins';
|
||||
import { loadNxPlugins } from '../../../../project-graph/plugins/internal-api';
|
||||
import { ProjectGraphBuilder } from '../../../../project-graph/project-graph-builder';
|
||||
import {
|
||||
retrieveProjectConfigurations,
|
||||
@ -14,6 +13,10 @@ import {
|
||||
import { setupWorkspaceContext } from '../../../../utils/workspace-context';
|
||||
import { buildExplicitTypeScriptDependencies } from './explicit-project-dependencies';
|
||||
import { TargetProjectLocator } from './target-project-locator';
|
||||
import {
|
||||
cleanupPlugins,
|
||||
getOnlyDefaultPlugins,
|
||||
} from '../../../../project-graph/plugins/get-plugins';
|
||||
|
||||
// projectName => tsconfig import path
|
||||
const dependencyProjectNamesToImportPaths = {
|
||||
@ -698,13 +701,13 @@ async function createContext(
|
||||
|
||||
setupWorkspaceContext(tempFs.tempDir);
|
||||
|
||||
const [plugins, cleanup] = await loadNxPlugins([], tempFs.tempDir);
|
||||
const plugins = await getOnlyDefaultPlugins(tempFs.tempDir);
|
||||
const { projects, projectRootMap } = await retrieveProjectConfigurations(
|
||||
plugins,
|
||||
tempFs.tempDir,
|
||||
nxJson
|
||||
);
|
||||
cleanup();
|
||||
cleanupPlugins();
|
||||
|
||||
const { fileMap } = await retrieveWorkspaceFiles(
|
||||
tempFs.tempDir,
|
||||
|
||||
@ -12,7 +12,7 @@ import {
|
||||
} from './nx-deps-cache';
|
||||
import { applyImplicitDependencies } from './utils/implicit-project-dependencies';
|
||||
import { normalizeProjectNodes } from './utils/normalize-project-nodes';
|
||||
import { LoadedNxPlugin } from './plugins/internal-api';
|
||||
import type { LoadedNxPlugin } from './plugins/loaded-nx-plugin';
|
||||
import {
|
||||
CreateDependenciesContext,
|
||||
CreateMetadataContext,
|
||||
|
||||
@ -1,17 +1,33 @@
|
||||
import { hashObject } from '../../hasher/file-hasher';
|
||||
import { readNxJson } from '../../config/nx-json';
|
||||
import { LoadedNxPlugin, loadNxPlugins } from './internal-api';
|
||||
import { workspaceRoot } from '../../utils/workspace-root';
|
||||
import { join } from 'node:path';
|
||||
|
||||
import { shouldMergeAngularProjects } from '../../adapter/angular-json';
|
||||
import { PluginConfiguration, readNxJson } from '../../config/nx-json';
|
||||
import { hashObject } from '../../hasher/file-hasher';
|
||||
import { IS_WASM } from '../../native';
|
||||
import { workspaceRoot } from '../../utils/workspace-root';
|
||||
import { loadNxPluginInIsolation } from './isolation';
|
||||
import { loadNxPlugin } from './in-process-loader';
|
||||
|
||||
import type { LoadedNxPlugin } from './loaded-nx-plugin';
|
||||
import {
|
||||
cleanupPluginTSTranspiler,
|
||||
pluginTranspilerIsRegistered,
|
||||
} from './transpiler';
|
||||
|
||||
/**
|
||||
* Stuff for specified NX Plugins.
|
||||
*/
|
||||
let currentPluginsConfigurationHash: string;
|
||||
let loadedPlugins: LoadedNxPlugin[];
|
||||
let pendingPluginsPromise:
|
||||
| Promise<readonly [LoadedNxPlugin[], () => void]>
|
||||
| undefined;
|
||||
let cleanup: () => void;
|
||||
let cleanup: () => void | undefined;
|
||||
|
||||
export async function getPlugins() {
|
||||
const pluginsConfiguration = readNxJson().plugins ?? [];
|
||||
export async function getPlugins(
|
||||
root = workspaceRoot
|
||||
): Promise<LoadedNxPlugin[]> {
|
||||
const pluginsConfiguration = readNxJson(root).plugins ?? [];
|
||||
const pluginsConfigurationHash = hashObject(pluginsConfiguration);
|
||||
|
||||
// If the plugins configuration has not changed, reuse the current plugins
|
||||
@ -28,22 +44,29 @@ export async function getPlugins() {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
pendingPluginsPromise ??= loadNxPlugins(pluginsConfiguration, workspaceRoot);
|
||||
pendingPluginsPromise ??= loadSpecifiedNxPlugins(pluginsConfiguration, root);
|
||||
|
||||
currentPluginsConfigurationHash = pluginsConfigurationHash;
|
||||
const [result, cleanupFn] = await pendingPluginsPromise;
|
||||
const [[result, cleanupFn], defaultPlugins] = await Promise.all([
|
||||
pendingPluginsPromise,
|
||||
getOnlyDefaultPlugins(root),
|
||||
]);
|
||||
cleanup = cleanupFn;
|
||||
loadedPlugins = result;
|
||||
return result;
|
||||
loadedPlugins = result.concat(defaultPlugins);
|
||||
return loadedPlugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stuff for default NX Plugins.
|
||||
*/
|
||||
|
||||
let loadedDefaultPlugins: LoadedNxPlugin[];
|
||||
let cleanupDefaultPlugins: () => void;
|
||||
let pendingDefaultPluginPromise:
|
||||
| Promise<readonly [LoadedNxPlugin[], () => void]>
|
||||
| undefined;
|
||||
|
||||
export async function getOnlyDefaultPlugins() {
|
||||
export async function getOnlyDefaultPlugins(root = workspaceRoot) {
|
||||
// If the plugins configuration has not changed, reuse the current plugins
|
||||
if (loadedDefaultPlugins) {
|
||||
return loadedPlugins;
|
||||
@ -55,7 +78,7 @@ export async function getOnlyDefaultPlugins() {
|
||||
cleanupDefaultPlugins();
|
||||
}
|
||||
|
||||
pendingDefaultPluginPromise ??= loadNxPlugins([], workspaceRoot);
|
||||
pendingDefaultPluginPromise ??= loadDefaultNxPlugins(workspaceRoot);
|
||||
|
||||
const [result, cleanupFn] = await pendingDefaultPluginPromise;
|
||||
cleanupDefaultPlugins = cleanupFn;
|
||||
@ -66,6 +89,138 @@ export async function getOnlyDefaultPlugins() {
|
||||
export function cleanupPlugins() {
|
||||
pendingPluginsPromise = undefined;
|
||||
pendingDefaultPluginPromise = undefined;
|
||||
cleanup();
|
||||
cleanupDefaultPlugins();
|
||||
cleanup?.();
|
||||
cleanupDefaultPlugins?.();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stuff for generic loading
|
||||
*/
|
||||
|
||||
function isIsolationEnabled() {
|
||||
// Explicitly enabled, regardless of further conditions
|
||||
if (process.env.NX_ISOLATE_PLUGINS === 'true') {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
// Explicitly disabled
|
||||
process.env.NX_ISOLATE_PLUGINS === 'false' ||
|
||||
// Isolation is disabled on WASM builds currently.
|
||||
IS_WASM
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
// Default value
|
||||
return true;
|
||||
}
|
||||
|
||||
const loadingMethod = isIsolationEnabled()
|
||||
? loadNxPluginInIsolation
|
||||
: loadNxPlugin;
|
||||
|
||||
async function loadDefaultNxPlugins(root = workspaceRoot) {
|
||||
performance.mark('loadDefaultNxPlugins:start');
|
||||
|
||||
const plugins = getDefaultPlugins(root);
|
||||
|
||||
const cleanupFunctions: Array<() => void> = [];
|
||||
const ret = [
|
||||
await Promise.all(
|
||||
plugins.map(async (plugin) => {
|
||||
performance.mark(`Load Nx Plugin: ${plugin} - start`);
|
||||
|
||||
const [loadedPluginPromise, cleanup] = await loadingMethod(
|
||||
plugin,
|
||||
root
|
||||
);
|
||||
|
||||
cleanupFunctions.push(cleanup);
|
||||
const res = await loadedPluginPromise;
|
||||
performance.mark(`Load Nx Plugin: ${plugin} - end`);
|
||||
performance.measure(
|
||||
`Load Nx Plugin: ${plugin}`,
|
||||
`Load Nx Plugin: ${plugin} - start`,
|
||||
`Load Nx Plugin: ${plugin} - end`
|
||||
);
|
||||
|
||||
return res;
|
||||
})
|
||||
),
|
||||
() => {
|
||||
for (const fn of cleanupFunctions) {
|
||||
fn();
|
||||
}
|
||||
if (pluginTranspilerIsRegistered()) {
|
||||
cleanupPluginTSTranspiler();
|
||||
}
|
||||
},
|
||||
] as const;
|
||||
performance.mark('loadDefaultNxPlugins:end');
|
||||
performance.measure(
|
||||
'loadDefaultNxPlugins',
|
||||
'loadDefaultNxPlugins:start',
|
||||
'loadDefaultNxPlugins:end'
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
async function loadSpecifiedNxPlugins(
|
||||
plugins: PluginConfiguration[],
|
||||
root = workspaceRoot
|
||||
): Promise<readonly [LoadedNxPlugin[], () => void]> {
|
||||
performance.mark('loadSpecifiedNxPlugins:start');
|
||||
|
||||
plugins ??= [];
|
||||
|
||||
const cleanupFunctions: Array<() => void> = [];
|
||||
const ret = [
|
||||
await Promise.all(
|
||||
plugins.map(async (plugin) => {
|
||||
const pluginPath = typeof plugin === 'string' ? plugin : plugin.plugin;
|
||||
performance.mark(`Load Nx Plugin: ${pluginPath} - start`);
|
||||
|
||||
const [loadedPluginPromise, cleanup] = await loadingMethod(
|
||||
plugin,
|
||||
root
|
||||
);
|
||||
|
||||
cleanupFunctions.push(cleanup);
|
||||
const res = await loadedPluginPromise;
|
||||
performance.mark(`Load Nx Plugin: ${pluginPath} - end`);
|
||||
performance.measure(
|
||||
`Load Nx Plugin: ${pluginPath}`,
|
||||
`Load Nx Plugin: ${pluginPath} - start`,
|
||||
`Load Nx Plugin: ${pluginPath} - end`
|
||||
);
|
||||
|
||||
return res;
|
||||
})
|
||||
),
|
||||
() => {
|
||||
for (const fn of cleanupFunctions) {
|
||||
fn();
|
||||
}
|
||||
if (pluginTranspilerIsRegistered()) {
|
||||
cleanupPluginTSTranspiler();
|
||||
}
|
||||
},
|
||||
] as const;
|
||||
performance.mark('loadSpecifiedNxPlugins:end');
|
||||
performance.measure(
|
||||
'loadSpecifiedNxPlugins',
|
||||
'loadSpecifiedNxPlugins:start',
|
||||
'loadSpecifiedNxPlugins:end'
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getDefaultPlugins(root: string) {
|
||||
return [
|
||||
join(__dirname, '../../plugins/js'),
|
||||
...(shouldMergeAngularProjects(root, false)
|
||||
? [join(__dirname, '../../adapter/angular-json')]
|
||||
: []),
|
||||
join(__dirname, '../../plugins/package-json'),
|
||||
join(__dirname, '../../plugins/project-json/build-nodes/project-json'),
|
||||
];
|
||||
}
|
||||
|
||||
85
packages/nx/src/project-graph/plugins/in-process-loader.ts
Normal file
85
packages/nx/src/project-graph/plugins/in-process-loader.ts
Normal file
@ -0,0 +1,85 @@
|
||||
// This file contains methods and utilities that should **only** be used by the plugin worker.
|
||||
|
||||
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||
|
||||
import { getNxRequirePaths } from '../../utils/installation-directory';
|
||||
import {
|
||||
PackageJson,
|
||||
readModulePackageJsonWithoutFallbacks,
|
||||
} from '../../utils/package-json';
|
||||
import { readJsonFile } from '../../utils/fileutils';
|
||||
|
||||
import type { PluginConfiguration } from '../../config/nx-json';
|
||||
import type { LoadedNxPlugin } from './loaded-nx-plugin';
|
||||
import { LoadPluginError } from '../error-types';
|
||||
import path = require('node:path/posix');
|
||||
import { loadResolvedNxPluginAsync } from './load-resolved-plugin';
|
||||
import { resolveLocalNxPlugin, resolveNxPlugin } from './resolve-plugin';
|
||||
import {
|
||||
pluginTranspilerIsRegistered,
|
||||
registerPluginTSTranspiler,
|
||||
} from './transpiler';
|
||||
|
||||
export function readPluginPackageJson(
|
||||
pluginName: string,
|
||||
projects: Record<string, ProjectConfiguration>,
|
||||
paths = getNxRequirePaths()
|
||||
): {
|
||||
path: string;
|
||||
json: PackageJson;
|
||||
} {
|
||||
try {
|
||||
const result = readModulePackageJsonWithoutFallbacks(pluginName, paths);
|
||||
return {
|
||||
json: result.packageJson,
|
||||
path: result.path,
|
||||
};
|
||||
} catch (e) {
|
||||
if (e.code === 'MODULE_NOT_FOUND') {
|
||||
const localPluginPath = resolveLocalNxPlugin(pluginName, projects);
|
||||
if (localPluginPath) {
|
||||
const localPluginPackageJson = path.join(
|
||||
localPluginPath.path,
|
||||
'package.json'
|
||||
);
|
||||
if (pluginTranspilerIsRegistered()) {
|
||||
registerPluginTSTranspiler();
|
||||
}
|
||||
return {
|
||||
path: localPluginPackageJson,
|
||||
json: readJsonFile(localPluginPackageJson),
|
||||
};
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
export function loadNxPlugin(plugin: PluginConfiguration, root: string) {
|
||||
return [
|
||||
loadNxPluginAsync(plugin, getNxRequirePaths(root), root),
|
||||
() => {},
|
||||
] as const;
|
||||
}
|
||||
|
||||
export async function loadNxPluginAsync(
|
||||
pluginConfiguration: PluginConfiguration,
|
||||
paths: string[],
|
||||
root: string
|
||||
): Promise<LoadedNxPlugin> {
|
||||
const moduleName =
|
||||
typeof pluginConfiguration === 'string'
|
||||
? pluginConfiguration
|
||||
: pluginConfiguration.plugin;
|
||||
try {
|
||||
const { pluginPath, name, shouldRegisterTSTranspiler } =
|
||||
await resolveNxPlugin(moduleName, root, paths);
|
||||
|
||||
if (shouldRegisterTSTranspiler) {
|
||||
registerPluginTSTranspiler();
|
||||
}
|
||||
return loadResolvedNxPluginAsync(pluginConfiguration, pluginPath, name);
|
||||
} catch (e) {
|
||||
throw new LoadPluginError(moduleName, e);
|
||||
}
|
||||
}
|
||||
@ -2,5 +2,6 @@ export * from './public-api';
|
||||
|
||||
// export * from './get-plugins';
|
||||
|
||||
export { readPluginPackageJson, registerPluginTSTranspiler } from './loader';
|
||||
export { readPluginPackageJson } from './in-process-loader';
|
||||
export { registerPluginTSTranspiler } from './transpiler';
|
||||
export { createNodesFromFiles } from './utils';
|
||||
|
||||
@ -1,224 +0,0 @@
|
||||
// This file contains the bits and bobs of the internal API for loading and interacting with Nx plugins.
|
||||
// For the public API, used by plugin authors, see `./public-api.ts`.
|
||||
|
||||
import { join } from 'path';
|
||||
|
||||
import { workspaceRoot } from '../../utils/workspace-root';
|
||||
import { PluginConfiguration } from '../../config/nx-json';
|
||||
import { shouldMergeAngularProjects } from '../../adapter/angular-json';
|
||||
|
||||
import {
|
||||
CreateDependencies,
|
||||
CreateDependenciesContext,
|
||||
CreateMetadata,
|
||||
CreateMetadataContext,
|
||||
CreateNodesContextV2,
|
||||
CreateNodesResult,
|
||||
NxPluginV2,
|
||||
ProjectsMetadata,
|
||||
} from './public-api';
|
||||
import { ProjectGraph } from '../../config/project-graph';
|
||||
import { loadNxPluginInIsolation } from './isolation';
|
||||
import { loadNxPlugin, unregisterPluginTSTranspiler } from './loader';
|
||||
import { createNodesFromFiles } from './utils';
|
||||
import {
|
||||
AggregateCreateNodesError,
|
||||
isAggregateCreateNodesError,
|
||||
} from '../error-types';
|
||||
import { IS_WASM } from '../../native';
|
||||
import { RawProjectGraphDependency } from '../project-graph-builder';
|
||||
|
||||
export class LoadedNxPlugin {
|
||||
readonly name: string;
|
||||
readonly createNodes?: [
|
||||
filePattern: string,
|
||||
// The create nodes function takes all matched files instead of just one, and includes
|
||||
// the result's context.
|
||||
fn: (
|
||||
matchedFiles: string[],
|
||||
context: CreateNodesContextV2
|
||||
) => Promise<
|
||||
Array<readonly [plugin: string, file: string, result: CreateNodesResult]>
|
||||
>
|
||||
];
|
||||
readonly createDependencies?: (
|
||||
context: CreateDependenciesContext
|
||||
) => Promise<RawProjectGraphDependency[]>;
|
||||
readonly createMetadata?: (
|
||||
graph: ProjectGraph,
|
||||
context: CreateMetadataContext
|
||||
) => Promise<ProjectsMetadata>;
|
||||
|
||||
readonly options?: unknown;
|
||||
readonly include?: string[];
|
||||
readonly exclude?: string[];
|
||||
|
||||
constructor(plugin: NxPluginV2, pluginDefinition: PluginConfiguration) {
|
||||
this.name = plugin.name;
|
||||
if (typeof pluginDefinition !== 'string') {
|
||||
this.options = pluginDefinition.options;
|
||||
this.include = pluginDefinition.include;
|
||||
this.exclude = pluginDefinition.exclude;
|
||||
}
|
||||
|
||||
if (plugin.createNodes && !plugin.createNodesV2) {
|
||||
this.createNodes = [
|
||||
plugin.createNodes[0],
|
||||
(configFiles, context) =>
|
||||
createNodesFromFiles(
|
||||
plugin.createNodes[1],
|
||||
configFiles,
|
||||
this.options,
|
||||
context
|
||||
).then((results) => results.map((r) => [this.name, r[0], r[1]])),
|
||||
];
|
||||
}
|
||||
|
||||
if (plugin.createNodesV2) {
|
||||
this.createNodes = [
|
||||
plugin.createNodesV2[0],
|
||||
async (configFiles, context) => {
|
||||
const result = await plugin.createNodesV2[1](
|
||||
configFiles,
|
||||
this.options,
|
||||
context
|
||||
);
|
||||
return result.map((r) => [this.name, r[0], r[1]]);
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (this.createNodes) {
|
||||
const inner = this.createNodes[1];
|
||||
this.createNodes[1] = async (...args) => {
|
||||
performance.mark(`${plugin.name}:createNodes - start`);
|
||||
try {
|
||||
return await inner(...args);
|
||||
} catch (e) {
|
||||
if (isAggregateCreateNodesError(e)) {
|
||||
throw e;
|
||||
}
|
||||
// The underlying plugin errored out. We can't know any partial results.
|
||||
throw new AggregateCreateNodesError([[null, e]], []);
|
||||
} finally {
|
||||
performance.mark(`${plugin.name}:createNodes - end`);
|
||||
performance.measure(
|
||||
`${plugin.name}:createNodes`,
|
||||
`${plugin.name}:createNodes - start`,
|
||||
`${plugin.name}:createNodes - end`
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (plugin.createDependencies) {
|
||||
this.createDependencies = async (context) =>
|
||||
plugin.createDependencies(this.options, context);
|
||||
}
|
||||
|
||||
if (plugin.createMetadata) {
|
||||
this.createMetadata = async (graph, context) =>
|
||||
plugin.createMetadata(graph, this.options, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type CreateNodesResultWithContext = CreateNodesResult & {
|
||||
file: string;
|
||||
pluginName: string;
|
||||
};
|
||||
|
||||
function isIsolationEnabled() {
|
||||
// Explicitly enabled, regardless of further conditions
|
||||
if (process.env.NX_ISOLATE_PLUGINS === 'true') {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
// Explicitly disabled
|
||||
process.env.NX_ISOLATE_PLUGINS === 'false' ||
|
||||
// Isolation is disabled on WASM builds currently.
|
||||
IS_WASM
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
// Default value
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use `getPlugins` instead.
|
||||
* @deprecated Do not use this. Use `getPlugins` instead.
|
||||
*/
|
||||
export async function loadNxPlugins(
|
||||
plugins: PluginConfiguration[],
|
||||
root = workspaceRoot
|
||||
): Promise<readonly [LoadedNxPlugin[], () => void]> {
|
||||
performance.mark('loadNxPlugins:start');
|
||||
const loadingMethod = isIsolationEnabled()
|
||||
? loadNxPluginInIsolation
|
||||
: loadNxPlugin;
|
||||
|
||||
plugins = await normalizePlugins(plugins, root);
|
||||
|
||||
const cleanupFunctions: Array<() => void> = [];
|
||||
const ret = [
|
||||
await Promise.all(
|
||||
plugins.map(async (plugin) => {
|
||||
const pluginPath = typeof plugin === 'string' ? plugin : plugin.plugin;
|
||||
performance.mark(`Load Nx Plugin: ${pluginPath} - start`);
|
||||
|
||||
const [loadedPluginPromise, cleanup] = await loadingMethod(
|
||||
plugin,
|
||||
root
|
||||
);
|
||||
|
||||
cleanupFunctions.push(cleanup);
|
||||
const res = await loadedPluginPromise;
|
||||
performance.mark(`Load Nx Plugin: ${pluginPath} - end`);
|
||||
performance.measure(
|
||||
`Load Nx Plugin: ${pluginPath}`,
|
||||
`Load Nx Plugin: ${pluginPath} - start`,
|
||||
`Load Nx Plugin: ${pluginPath} - end`
|
||||
);
|
||||
|
||||
return res;
|
||||
})
|
||||
),
|
||||
() => {
|
||||
for (const fn of cleanupFunctions) {
|
||||
fn();
|
||||
}
|
||||
if (unregisterPluginTSTranspiler) {
|
||||
unregisterPluginTSTranspiler();
|
||||
}
|
||||
},
|
||||
] as const;
|
||||
performance.mark('loadNxPlugins:end');
|
||||
performance.measure(
|
||||
'loadNxPlugins',
|
||||
'loadNxPlugins:start',
|
||||
'loadNxPlugins:end'
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
async function normalizePlugins(plugins: PluginConfiguration[], root: string) {
|
||||
plugins ??= [];
|
||||
|
||||
return [
|
||||
...plugins,
|
||||
// Most of the nx core node plugins go on the end, s.t. it overwrites any other plugins
|
||||
...(await getDefaultPlugins(root)),
|
||||
];
|
||||
}
|
||||
|
||||
export async function getDefaultPlugins(root: string) {
|
||||
return [
|
||||
join(__dirname, '../../plugins/js'),
|
||||
...(shouldMergeAngularProjects(root, false)
|
||||
? [join(__dirname, '../../adapter/angular-json')]
|
||||
: []),
|
||||
join(__dirname, '../../plugins/package-json'),
|
||||
join(__dirname, '../../plugins/project-json/build-nodes/project-json'),
|
||||
];
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import { workspaceRoot } from '../../../utils/workspace-root';
|
||||
import { PluginConfiguration } from '../../../config/nx-json';
|
||||
import { LoadedNxPlugin } from '../internal-api';
|
||||
import type { PluginConfiguration } from '../../../config/nx-json';
|
||||
import type { LoadedNxPlugin } from '../loaded-nx-plugin';
|
||||
import { loadRemoteNxPlugin } from './plugin-pool';
|
||||
|
||||
export async function loadNxPluginInIsolation(
|
||||
|
||||
@ -5,7 +5,7 @@ import {
|
||||
CreateMetadataContext,
|
||||
CreateNodesContextV2,
|
||||
} from '../public-api';
|
||||
import { LoadedNxPlugin } from '../internal-api';
|
||||
import type { LoadedNxPlugin } from '../loaded-nx-plugin';
|
||||
import { Serializable } from 'child_process';
|
||||
import { Socket } from 'net';
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import { PluginConfiguration } from '../../../config/nx-json';
|
||||
// TODO (@AgentEnder): After scoped verbose logging is implemented, re-add verbose logs here.
|
||||
// import { logger } from '../../utils/logger';
|
||||
|
||||
import { LoadedNxPlugin } from '../internal-api';
|
||||
import type { LoadedNxPlugin } from '../loaded-nx-plugin';
|
||||
import { getPluginOsSocketPath } from '../../../daemon/socket-utils';
|
||||
import { consumeMessagesFromSocket } from '../../../utils/consume-messages-from-socket';
|
||||
|
||||
@ -17,7 +17,7 @@ import {
|
||||
sendMessageOverSocket,
|
||||
} from './messaging';
|
||||
import { getNxRequirePaths } from '../../../utils/installation-directory';
|
||||
import { resolveNxPlugin } from '../loader';
|
||||
import { resolveNxPlugin } from '../resolve-plugin';
|
||||
|
||||
const cleanupFunctions = new Set<() => void>();
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@ import { consumeMessagesFromSocket } from '../../../utils/consume-messages-from-
|
||||
|
||||
import { createServer } from 'net';
|
||||
import { unlinkSync } from 'fs';
|
||||
import { registerPluginTSTranspiler } from '../loader';
|
||||
|
||||
if (process.env.NX_PERF_LOGGING === 'true') {
|
||||
require('../../../utils/perf-logging');
|
||||
@ -53,7 +52,9 @@ const server = createServer((socket) => {
|
||||
// Register the ts-transpiler if we are pointing to a
|
||||
// plain ts file that's not part of a plugin project
|
||||
if (shouldRegisterTSTranspiler) {
|
||||
registerPluginTSTranspiler();
|
||||
(
|
||||
require('../transpiler') as typeof import('../transpiler')
|
||||
).registerPluginTSTranspiler();
|
||||
}
|
||||
plugin = await loadResolvedNxPluginAsync(
|
||||
pluginConfiguration,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { PluginConfiguration } from '../../config/nx-json';
|
||||
import { LoadedNxPlugin } from './internal-api';
|
||||
import { LoadedNxPlugin } from './loaded-nx-plugin';
|
||||
import { NxPlugin } from './public-api';
|
||||
|
||||
export async function loadResolvedNxPluginAsync(
|
||||
|
||||
116
packages/nx/src/project-graph/plugins/loaded-nx-plugin.ts
Normal file
116
packages/nx/src/project-graph/plugins/loaded-nx-plugin.ts
Normal file
@ -0,0 +1,116 @@
|
||||
import type { ProjectGraph } from '../../config/project-graph';
|
||||
import type { PluginConfiguration } from '../../config/nx-json';
|
||||
import {
|
||||
AggregateCreateNodesError,
|
||||
isAggregateCreateNodesError,
|
||||
} from '../error-types';
|
||||
import type { RawProjectGraphDependency } from '../project-graph-builder';
|
||||
import type {
|
||||
CreateDependenciesContext,
|
||||
CreateMetadataContext,
|
||||
CreateNodesContextV2,
|
||||
CreateNodesResult,
|
||||
NxPluginV2,
|
||||
ProjectsMetadata,
|
||||
} from './public-api';
|
||||
import { createNodesFromFiles } from './utils';
|
||||
|
||||
export class LoadedNxPlugin {
|
||||
readonly name: string;
|
||||
readonly createNodes?: [
|
||||
filePattern: string,
|
||||
// The create nodes function takes all matched files instead of just one, and includes
|
||||
// the result's context.
|
||||
fn: (
|
||||
matchedFiles: string[],
|
||||
context: CreateNodesContextV2
|
||||
) => Promise<
|
||||
Array<readonly [plugin: string, file: string, result: CreateNodesResult]>
|
||||
>
|
||||
];
|
||||
readonly createDependencies?: (
|
||||
context: CreateDependenciesContext
|
||||
) => Promise<RawProjectGraphDependency[]>;
|
||||
readonly createMetadata?: (
|
||||
graph: ProjectGraph,
|
||||
context: CreateMetadataContext
|
||||
) => Promise<ProjectsMetadata>;
|
||||
|
||||
readonly options?: unknown;
|
||||
readonly include?: string[];
|
||||
readonly exclude?: string[];
|
||||
|
||||
constructor(plugin: NxPluginV2, pluginDefinition: PluginConfiguration) {
|
||||
this.name = plugin.name;
|
||||
if (typeof pluginDefinition !== 'string') {
|
||||
this.options = pluginDefinition.options;
|
||||
this.include = pluginDefinition.include;
|
||||
this.exclude = pluginDefinition.exclude;
|
||||
}
|
||||
|
||||
if (plugin.createNodes && !plugin.createNodesV2) {
|
||||
this.createNodes = [
|
||||
plugin.createNodes[0],
|
||||
(configFiles, context) =>
|
||||
createNodesFromFiles(
|
||||
plugin.createNodes[1],
|
||||
configFiles,
|
||||
this.options,
|
||||
context
|
||||
).then((results) => results.map((r) => [this.name, r[0], r[1]])),
|
||||
];
|
||||
}
|
||||
|
||||
if (plugin.createNodesV2) {
|
||||
this.createNodes = [
|
||||
plugin.createNodesV2[0],
|
||||
async (configFiles, context) => {
|
||||
const result = await plugin.createNodesV2[1](
|
||||
configFiles,
|
||||
this.options,
|
||||
context
|
||||
);
|
||||
return result.map((r) => [this.name, r[0], r[1]]);
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (this.createNodes) {
|
||||
const inner = this.createNodes[1];
|
||||
this.createNodes[1] = async (...args) => {
|
||||
performance.mark(`${plugin.name}:createNodes - start`);
|
||||
try {
|
||||
return await inner(...args);
|
||||
} catch (e) {
|
||||
if (isAggregateCreateNodesError(e)) {
|
||||
throw e;
|
||||
}
|
||||
// The underlying plugin errored out. We can't know any partial results.
|
||||
throw new AggregateCreateNodesError([[null, e]], []);
|
||||
} finally {
|
||||
performance.mark(`${plugin.name}:createNodes - end`);
|
||||
performance.measure(
|
||||
`${plugin.name}:createNodes`,
|
||||
`${plugin.name}:createNodes - start`,
|
||||
`${plugin.name}:createNodes - end`
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (plugin.createDependencies) {
|
||||
this.createDependencies = async (context) =>
|
||||
plugin.createDependencies(this.options, context);
|
||||
}
|
||||
|
||||
if (plugin.createMetadata) {
|
||||
this.createMetadata = async (graph, context) =>
|
||||
plugin.createMetadata(graph, this.options, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type CreateNodesResultWithContext = CreateNodesResult & {
|
||||
file: string;
|
||||
pluginName: string;
|
||||
};
|
||||
@ -1,71 +1,59 @@
|
||||
// This file contains methods and utilities that should **only** be used by the plugin worker.
|
||||
|
||||
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||
|
||||
import { join } from 'node:path/posix';
|
||||
import { getNxRequirePaths } from '../../utils/installation-directory';
|
||||
import {
|
||||
PackageJson,
|
||||
readModulePackageJsonWithoutFallbacks,
|
||||
} from '../../utils/package-json';
|
||||
import { readJsonFile } from '../../utils/fileutils';
|
||||
import { workspaceRoot } from '../../utils/workspace-root';
|
||||
import * as path from 'node:path';
|
||||
import { existsSync } from 'node:fs';
|
||||
import {
|
||||
registerTranspiler,
|
||||
registerTsConfigPaths,
|
||||
} from '../../plugins/js/utils/register';
|
||||
import {
|
||||
ProjectRootMappings,
|
||||
findProjectForPath,
|
||||
} from '../utils/find-project-for-path';
|
||||
import { normalizePath } from '../../utils/path';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
import type * as ts from 'typescript';
|
||||
import { extname } from 'node:path';
|
||||
import type { PluginConfiguration } from '../../config/nx-json';
|
||||
import { retrieveProjectConfigurationsWithoutPluginInference } from '../utils/retrieve-workspace-files';
|
||||
import { LoadedNxPlugin } from './internal-api';
|
||||
import { LoadPluginError } from '../error-types';
|
||||
import path = require('node:path/posix');
|
||||
import { readTsConfig } from '../../plugins/js/utils/typescript';
|
||||
import { loadResolvedNxPluginAsync } from './load-resolved-plugin';
|
||||
import { getPackageEntryPointsToProjectMap } from '../../plugins/js/utils/packages';
|
||||
import { readJsonFile } from '../../utils/fileutils';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { normalizePath } from '../../utils/path';
|
||||
import { workspaceRoot } from '../../utils/workspace-root';
|
||||
import {
|
||||
findProjectForPath,
|
||||
ProjectRootMappings,
|
||||
} from '../utils/find-project-for-path';
|
||||
import { retrieveProjectConfigurationsWithoutPluginInference } from '../utils/retrieve-workspace-files';
|
||||
|
||||
export function readPluginPackageJson(
|
||||
pluginName: string,
|
||||
projects: Record<string, ProjectConfiguration>,
|
||||
paths = getNxRequirePaths()
|
||||
): {
|
||||
path: string;
|
||||
json: PackageJson;
|
||||
} {
|
||||
import type { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||
|
||||
let projectsWithoutInference: Record<string, ProjectConfiguration>;
|
||||
|
||||
export async function resolveNxPlugin(
|
||||
moduleName: string,
|
||||
root: string,
|
||||
paths: string[]
|
||||
) {
|
||||
try {
|
||||
const result = readModulePackageJsonWithoutFallbacks(pluginName, paths);
|
||||
return {
|
||||
json: result.packageJson,
|
||||
path: result.path,
|
||||
};
|
||||
} catch (e) {
|
||||
if (e.code === 'MODULE_NOT_FOUND') {
|
||||
const localPluginPath = resolveLocalNxPlugin(pluginName, projects);
|
||||
if (localPluginPath) {
|
||||
const localPluginPackageJson = path.join(
|
||||
localPluginPath.path,
|
||||
'package.json'
|
||||
);
|
||||
if (!unregisterPluginTSTranspiler) {
|
||||
registerPluginTSTranspiler();
|
||||
}
|
||||
return {
|
||||
path: localPluginPackageJson,
|
||||
json: readJsonFile(localPluginPackageJson),
|
||||
};
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
require.resolve(moduleName);
|
||||
} catch {
|
||||
// If a plugin cannot be resolved, we will need projects to resolve it
|
||||
projectsWithoutInference ??=
|
||||
await retrieveProjectConfigurationsWithoutPluginInference(root);
|
||||
}
|
||||
const { pluginPath, name, shouldRegisterTSTranspiler } = getPluginPathAndName(
|
||||
moduleName,
|
||||
paths,
|
||||
projectsWithoutInference,
|
||||
root
|
||||
);
|
||||
return { pluginPath, name, shouldRegisterTSTranspiler };
|
||||
}
|
||||
|
||||
function readPluginMainFromProjectConfiguration(
|
||||
plugin: ProjectConfiguration
|
||||
): string | null {
|
||||
const { main } =
|
||||
Object.values(plugin.targets).find((x) =>
|
||||
[
|
||||
'@nx/js:tsc',
|
||||
'@nrwl/js:tsc',
|
||||
'@nx/js:swc',
|
||||
'@nrwl/js:swc',
|
||||
'@nx/node:package',
|
||||
'@nrwl/node:package',
|
||||
].includes(x.executor)
|
||||
)?.options ||
|
||||
plugin.targets?.build?.options ||
|
||||
{};
|
||||
return main;
|
||||
}
|
||||
|
||||
export function resolveLocalNxPlugin(
|
||||
@ -76,40 +64,45 @@ export function resolveLocalNxPlugin(
|
||||
return lookupLocalPlugin(importPath, projects, root);
|
||||
}
|
||||
|
||||
export let unregisterPluginTSTranspiler: (() => void) | null = null;
|
||||
|
||||
/**
|
||||
* Register swc-node or ts-node if they are not currently registered
|
||||
* with some default settings which work well for Nx plugins.
|
||||
*/
|
||||
export function registerPluginTSTranspiler() {
|
||||
// Get the first tsconfig that matches the allowed set
|
||||
const tsConfigName = [
|
||||
join(workspaceRoot, 'tsconfig.base.json'),
|
||||
join(workspaceRoot, 'tsconfig.json'),
|
||||
].find((x) => existsSync(x));
|
||||
|
||||
if (!tsConfigName) {
|
||||
return;
|
||||
export function getPluginPathAndName(
|
||||
moduleName: string,
|
||||
paths: string[],
|
||||
projects: Record<string, ProjectConfiguration>,
|
||||
root: string
|
||||
) {
|
||||
let pluginPath: string;
|
||||
let shouldRegisterTSTranspiler = false;
|
||||
try {
|
||||
pluginPath = require.resolve(moduleName, {
|
||||
paths,
|
||||
});
|
||||
const extension = path.extname(pluginPath);
|
||||
shouldRegisterTSTranspiler = extension === '.ts';
|
||||
} catch (e) {
|
||||
if (e.code === 'MODULE_NOT_FOUND') {
|
||||
const plugin = resolveLocalNxPlugin(moduleName, projects, root);
|
||||
if (plugin) {
|
||||
shouldRegisterTSTranspiler = true;
|
||||
const main = readPluginMainFromProjectConfiguration(
|
||||
plugin.projectConfig
|
||||
);
|
||||
pluginPath = main ? path.join(root, main) : plugin.path;
|
||||
} else {
|
||||
logger.error(`Plugin listed in \`nx.json\` not found: ${moduleName}`);
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
const packageJsonPath = path.join(pluginPath, 'package.json');
|
||||
|
||||
const tsConfig: Partial<ts.ParsedCommandLine> = tsConfigName
|
||||
? readTsConfig(tsConfigName)
|
||||
: {};
|
||||
const cleanupFns = [
|
||||
registerTsConfigPaths(tsConfigName),
|
||||
registerTranspiler(
|
||||
{
|
||||
experimentalDecorators: true,
|
||||
emitDecoratorMetadata: true,
|
||||
...tsConfig.options,
|
||||
},
|
||||
tsConfig.raw
|
||||
),
|
||||
];
|
||||
unregisterPluginTSTranspiler = () => {
|
||||
cleanupFns.forEach((fn) => fn?.());
|
||||
};
|
||||
const { name } =
|
||||
!['.ts', '.js'].some((x) => path.extname(moduleName) === x) && // Not trying to point to a ts or js file
|
||||
existsSync(packageJsonPath) // plugin has a package.json
|
||||
? readJsonFile(packageJsonPath) // read name from package.json
|
||||
: { name: moduleName };
|
||||
return { pluginPath, name, shouldRegisterTSTranspiler };
|
||||
}
|
||||
|
||||
function lookupLocalPlugin(
|
||||
@ -184,115 +177,3 @@ function readTsConfigPaths(root: string = workspaceRoot) {
|
||||
}
|
||||
return tsconfigPaths ?? {};
|
||||
}
|
||||
|
||||
function readPluginMainFromProjectConfiguration(
|
||||
plugin: ProjectConfiguration
|
||||
): string | null {
|
||||
const { main } =
|
||||
Object.values(plugin.targets).find((x) =>
|
||||
[
|
||||
'@nx/js:tsc',
|
||||
'@nrwl/js:tsc',
|
||||
'@nx/js:swc',
|
||||
'@nrwl/js:swc',
|
||||
'@nx/node:package',
|
||||
'@nrwl/node:package',
|
||||
].includes(x.executor)
|
||||
)?.options ||
|
||||
plugin.targets?.build?.options ||
|
||||
{};
|
||||
return main;
|
||||
}
|
||||
|
||||
export function getPluginPathAndName(
|
||||
moduleName: string,
|
||||
paths: string[],
|
||||
projects: Record<string, ProjectConfiguration>,
|
||||
root: string
|
||||
) {
|
||||
let pluginPath: string;
|
||||
let shouldRegisterTSTranspiler = false;
|
||||
try {
|
||||
pluginPath = require.resolve(moduleName, {
|
||||
paths,
|
||||
});
|
||||
const extension = path.extname(pluginPath);
|
||||
shouldRegisterTSTranspiler = extension === '.ts';
|
||||
} catch (e) {
|
||||
if (e.code === 'MODULE_NOT_FOUND') {
|
||||
const plugin = resolveLocalNxPlugin(moduleName, projects, root);
|
||||
if (plugin) {
|
||||
shouldRegisterTSTranspiler = true;
|
||||
const main = readPluginMainFromProjectConfiguration(
|
||||
plugin.projectConfig
|
||||
);
|
||||
pluginPath = main ? path.join(root, main) : plugin.path;
|
||||
} else {
|
||||
logger.error(`Plugin listed in \`nx.json\` not found: ${moduleName}`);
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
const packageJsonPath = path.join(pluginPath, 'package.json');
|
||||
|
||||
const { name } =
|
||||
!['.ts', '.js'].some((x) => extname(moduleName) === x) && // Not trying to point to a ts or js file
|
||||
existsSync(packageJsonPath) // plugin has a package.json
|
||||
? readJsonFile(packageJsonPath) // read name from package.json
|
||||
: { name: moduleName };
|
||||
return { pluginPath, name, shouldRegisterTSTranspiler };
|
||||
}
|
||||
|
||||
let projectsWithoutInference: Record<string, ProjectConfiguration>;
|
||||
|
||||
export function loadNxPlugin(plugin: PluginConfiguration, root: string) {
|
||||
return [
|
||||
loadNxPluginAsync(plugin, getNxRequirePaths(root), root),
|
||||
() => {},
|
||||
] as const;
|
||||
}
|
||||
|
||||
export async function resolveNxPlugin(
|
||||
moduleName: string,
|
||||
root: string,
|
||||
paths: string[]
|
||||
) {
|
||||
try {
|
||||
require.resolve(moduleName);
|
||||
} catch {
|
||||
// If a plugin cannot be resolved, we will need projects to resolve it
|
||||
projectsWithoutInference ??=
|
||||
await retrieveProjectConfigurationsWithoutPluginInference(root);
|
||||
}
|
||||
const { pluginPath, name, shouldRegisterTSTranspiler } = getPluginPathAndName(
|
||||
moduleName,
|
||||
paths,
|
||||
projectsWithoutInference,
|
||||
root
|
||||
);
|
||||
return { pluginPath, name, shouldRegisterTSTranspiler };
|
||||
}
|
||||
|
||||
export async function loadNxPluginAsync(
|
||||
pluginConfiguration: PluginConfiguration,
|
||||
paths: string[],
|
||||
root: string
|
||||
): Promise<LoadedNxPlugin> {
|
||||
const moduleName =
|
||||
typeof pluginConfiguration === 'string'
|
||||
? pluginConfiguration
|
||||
: pluginConfiguration.plugin;
|
||||
try {
|
||||
const { pluginPath, name, shouldRegisterTSTranspiler } =
|
||||
await resolveNxPlugin(moduleName, root, paths);
|
||||
|
||||
if (shouldRegisterTSTranspiler) {
|
||||
registerPluginTSTranspiler();
|
||||
}
|
||||
return loadResolvedNxPluginAsync(pluginConfiguration, pluginPath, name);
|
||||
} catch (e) {
|
||||
throw new LoadPluginError(moduleName, e);
|
||||
}
|
||||
}
|
||||
54
packages/nx/src/project-graph/plugins/transpiler.ts
Normal file
54
packages/nx/src/project-graph/plugins/transpiler.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { existsSync } from 'node:fs';
|
||||
import { join } from 'node:path/posix';
|
||||
import { workspaceRoot } from '../../utils/workspace-root';
|
||||
import {
|
||||
registerTranspiler,
|
||||
registerTsConfigPaths,
|
||||
} from '../../plugins/js/utils/register';
|
||||
import { readTsConfig } from '../../plugins/js/utils/typescript';
|
||||
import type * as ts from 'typescript';
|
||||
|
||||
export let unregisterPluginTSTranspiler: (() => void) | null = null;
|
||||
|
||||
/**
|
||||
* Register swc-node or ts-node if they are not currently registered
|
||||
* with some default settings which work well for Nx plugins.
|
||||
*/
|
||||
export function registerPluginTSTranspiler() {
|
||||
// Get the first tsconfig that matches the allowed set
|
||||
const tsConfigName = [
|
||||
join(workspaceRoot, 'tsconfig.base.json'),
|
||||
join(workspaceRoot, 'tsconfig.json'),
|
||||
].find((x) => existsSync(x));
|
||||
|
||||
if (!tsConfigName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tsConfig: Partial<ts.ParsedCommandLine> = tsConfigName
|
||||
? readTsConfig(tsConfigName)
|
||||
: {};
|
||||
const cleanupFns = [
|
||||
registerTsConfigPaths(tsConfigName),
|
||||
registerTranspiler(
|
||||
{
|
||||
experimentalDecorators: true,
|
||||
emitDecoratorMetadata: true,
|
||||
...tsConfig.options,
|
||||
},
|
||||
tsConfig.raw
|
||||
),
|
||||
];
|
||||
unregisterPluginTSTranspiler = () => {
|
||||
cleanupFns.forEach((fn) => fn?.());
|
||||
};
|
||||
}
|
||||
|
||||
export function pluginTranspilerIsRegistered() {
|
||||
return unregisterPluginTSTranspiler !== null;
|
||||
}
|
||||
|
||||
export function cleanupPluginTSTranspiler() {
|
||||
unregisterPluginTSTranspiler?.();
|
||||
unregisterPluginTSTranspiler = null;
|
||||
}
|
||||
@ -15,7 +15,7 @@ import {
|
||||
readTargetDefaultsForTarget,
|
||||
} from './project-configuration-utils';
|
||||
import { NxPluginV2 } from '../plugins';
|
||||
import { LoadedNxPlugin } from '../plugins/internal-api';
|
||||
import { LoadedNxPlugin } from '../plugins/loaded-nx-plugin';
|
||||
import { dirname } from 'path';
|
||||
import { isProjectConfigurationsError } from '../error-types';
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ import { minimatch } from 'minimatch';
|
||||
import { join } from 'path';
|
||||
import { performance } from 'perf_hooks';
|
||||
|
||||
import { LoadedNxPlugin } from '../plugins/internal-api';
|
||||
import { LoadedNxPlugin } from '../plugins/loaded-nx-plugin';
|
||||
import {
|
||||
MergeNodesError,
|
||||
ProjectConfigurationsError,
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
ConfigurationResult,
|
||||
createProjectConfigurations,
|
||||
} from './project-configuration-utils';
|
||||
import { LoadedNxPlugin } from '../plugins/internal-api';
|
||||
import { LoadedNxPlugin } from '../plugins/loaded-nx-plugin';
|
||||
import {
|
||||
getNxWorkspaceFilesFromContext,
|
||||
globWithWorkspaceContext,
|
||||
|
||||
@ -7,9 +7,9 @@ import { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||
import { readJsonFile } from '../fileutils';
|
||||
import { getNxRequirePaths } from '../installation-directory';
|
||||
import { readPluginPackageJson } from '../../project-graph/plugins';
|
||||
import { loadNxPlugin } from '../../project-graph/plugins/loader';
|
||||
import { loadNxPlugin } from '../../project-graph/plugins/in-process-loader';
|
||||
import { PackageJson } from '../package-json';
|
||||
import { LoadedNxPlugin } from '../../project-graph/plugins/internal-api';
|
||||
import { LoadedNxPlugin } from '../../project-graph/plugins/loaded-nx-plugin';
|
||||
|
||||
export interface PluginCapabilities {
|
||||
name: string;
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
parseTargetString,
|
||||
} from '@nx/devkit';
|
||||
import { addE2eCiTargetDefaults as _addE2eCiTargetDefaults } from '@nx/devkit/src/generators/target-defaults-utils';
|
||||
import { LoadedNxPlugin } from 'nx/src/project-graph/plugins/internal-api';
|
||||
import { LoadedNxPlugin } from 'nx/src/project-graph/plugins/loaded-nx-plugin';
|
||||
import type { ConfigurationResult } from 'nx/src/project-graph/utils/project-configuration-utils';
|
||||
import {
|
||||
ProjectConfigurationsError,
|
||||
|
||||
@ -12,7 +12,7 @@ import {
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import addE2eCiTargetDefaults from './add-e2e-ci-target-defaults';
|
||||
import type { ConfigurationResult } from 'nx/src/project-graph/utils/project-configuration-utils';
|
||||
import { LoadedNxPlugin } from 'nx/src/project-graph/plugins/internal-api';
|
||||
import { LoadedNxPlugin } from 'nx/src/project-graph/plugins/loaded-nx-plugin';
|
||||
import { retrieveProjectConfigurations } from 'nx/src/project-graph/utils/retrieve-workspace-files';
|
||||
import { ProjectConfigurationsError } from 'nx/src/project-graph/error-types';
|
||||
import { createNodesV2 as webpackCreateNodesV2 } from '@nx/webpack/src/plugins/plugin';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user