Skip to content

Commit

Permalink
feat(core): ability to run local nx plugins generators and executors
Browse files Browse the repository at this point in the history
  • Loading branch information
AgentEnder committed Feb 18, 2022
1 parent 19efdfc commit b9fc67f
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 14 deletions.
21 changes: 18 additions & 3 deletions e2e/nx-plugin/src/nx-plugin.test.ts
Expand Up @@ -6,15 +6,15 @@ import {
newProject,
readJson,
readProjectConfig,
readWorkspaceConfig,
runCLI,
runCLIAsync,
uniq,
workspaceConfigName,
} from '@nrwl/e2e/utils';

describe('Nx Plugin', () => {
beforeEach(() => newProject());
beforeEach(() => {
newProject();
});

it('should be able to generate a Nx Plugin ', async () => {
const plugin = uniq('plugin');
Expand Down Expand Up @@ -172,6 +172,21 @@ describe('Nx Plugin', () => {
});
}, 90000);

it('should be able to use a local plugin', async () => {
const plugin = uniq('plugin');
const generator = uniq('generator');
const generatedProject = uniq('project');

runCLI(`generate @nrwl/nx-plugin:plugin ${plugin} --linter=eslint`);
runCLI(
`generate @nrwl/nx-plugin:generator ${generator} --project=${plugin}`
);

runCLI(`generate @proj/${plugin}:${generator} --name ${generatedProject}`);
expect(() => checkFilesExist(`libs/${generatedProject}`)).not.toThrow();
expect(() => runCLI(`build ${generatedProject}`)).not.toThrow();
}, 90000);

describe('--directory', () => {
it('should create a plugin in the specified directory', () => {
const plugin = uniq('plugin');
Expand Down
84 changes: 73 additions & 11 deletions packages/tao/src/shared/workspace.ts
Expand Up @@ -10,6 +10,9 @@ import ignore, { Ignore } from 'ignore';
import { basename, dirname, join } from 'path';
import { performance } from 'perf_hooks';
import { loadNxPlugins } from './nx-plugin';
import { PackageJson } from './package-json';
import { execSync } from 'child_process';
import { getPackageManagerCommand } from './package-manager';

export interface Workspace
extends WorkspaceJsonConfiguration,
Expand Down Expand Up @@ -402,10 +405,8 @@ export class Workspaces {
}

private readExecutorsJson(nodeModule: string, executor: string) {
const packageJsonPath = require.resolve(`${nodeModule}/package.json`, {
paths: this.resolvePaths(),
});
const packageJson = readJsonFile(packageJsonPath);
const { json: packageJson, path: packageJsonPath } =
this.readPluginPackageJson(nodeModule);
const executorsFile = packageJson.executors ?? packageJson.builders;

if (!executorsFile) {
Expand Down Expand Up @@ -440,13 +441,8 @@ export class Workspaces {
paths: this.resolvePaths(),
});
} else {
const packageJsonPath = require.resolve(
`${collectionName}/package.json`,
{
paths: this.resolvePaths(),
}
);
const packageJson = readJsonFile(packageJsonPath);
const { json: packageJson, path: packageJsonPath } =
this.readPluginPackageJson(collectionName);
const generatorsFile = packageJson.generators ?? packageJson.schematics;

if (!generatorsFile) {
Expand Down Expand Up @@ -479,6 +475,72 @@ export class Workspaces {
return { generatorsFilePath, generatorsJson, normalizedGeneratorName };
}

private readPluginPackageJson(pluginName: string): {
path: string;
json: PackageJson;
} {
let packageJsonPath: string;
try {
packageJsonPath = require.resolve(`${pluginName}/package.json`, {
paths: this.resolvePaths(),
});
} catch (e) {
if (e.code === 'MODULE_NOT_FOUND') {
return this.resolveLocalNxPlugin(pluginName);
} else {
throw e;
}
}
return { json: readJsonFile(packageJsonPath), path: packageJsonPath };
}

private resolveLocalNxPlugin(importPath: string) {
const workspace = this.readWorkspaceConfiguration();
const plugin = this.findNxProjectForImportPath(importPath, workspace);
const projectConfig = workspace.projects[plugin];
execSync(`${getPackageManagerCommand().exec} nx build ${plugin}`, {
cwd: this.root,
});
const packageJsonPath = join(
this.root,
projectConfig.targets?.build?.options?.outputPath,
'package.json'
);
return {
json: readJsonFile(packageJsonPath),
path: packageJsonPath,
};
}

private findNxProjectForImportPath(
importPath: string,
workspace: WorkspaceJsonConfiguration
) {
const tsConfigPaths: Record<string, string[]> = readJsonFile(
join(this.root, 'tsconfig.base.json')
)?.compilerOptions?.paths;
const possiblePaths = tsConfigPaths[importPath]?.map((p) =>
path.resolve(this.root, p)
);
if (tsConfigPaths[importPath]) {
const projectRootMappings = Object.entries(workspace.projects).reduce(
(m, [project, config]) => {
m[path.resolve(this.root, config.root)] = project;
return m;
},
{}
);
for (const root of Object.keys(projectRootMappings)) {
if (possiblePaths.some((p) => p.startsWith(root))) {
return projectRootMappings[root];
}
}
}
throw new Error(
'Unable to resolve local plugin with import path ' + importPath
);
}

private resolvePaths() {
return this.root ? [this.root, __dirname] : [__dirname];
}
Expand Down

0 comments on commit b9fc67f

Please sign in to comment.