Skip to content

Commit

Permalink
chore(core): swap to use swc-node for running typescript executors
Browse files Browse the repository at this point in the history
  • Loading branch information
AgentEnder committed Feb 25, 2022
1 parent e22d9d7 commit 2dd17ff
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 37 deletions.
10 changes: 8 additions & 2 deletions e2e/nx-plugin/src/nx-plugin.test.ts
Expand Up @@ -11,6 +11,8 @@ import {
uniq,
updateFile,
createFile,
readFile,
removeFile,
} from '@nrwl/e2e/utils';

describe('Nx Plugin', () => {
Expand Down Expand Up @@ -187,9 +189,10 @@ describe('Nx Plugin', () => {

updateFile(
`libs/${plugin}/src/index.ts`,
`
`import {basename} from 'path'
export function registerProjectTargets(f) {
if (f === 'my-project-file') {
if (basename(f) === 'my-project-file') {
return {
build: {
executor: "@nrwl/workspace:run-commands",
Expand All @@ -213,6 +216,8 @@ export const projectFilePatterns = ['my-project-file'];

const inferredProject = uniq('inferred');
createFile(`libs/${inferredProject}/my-project-file`);
const workspaceJsonContents = readFile('workspace.json');
removeFile('workspace.json');

runCLI(
`generate @${npmScope}/${plugin}:${generator} --name ${generatedProject}`
Expand All @@ -222,6 +227,7 @@ export const projectFilePatterns = ['my-project-file'];
expect(runCLI(`build ${inferredProject}`)).toContain(
'custom registered target'
);
createFile('workspace.json', workspaceJsonContents);
}, 90000);

describe('--directory', () => {
Expand Down
2 changes: 2 additions & 0 deletions packages/tao/package.json
Expand Up @@ -30,6 +30,8 @@
},
"homepage": "https://nx.dev",
"dependencies": {
"@swc-node/register": "^1.4.2",
"tsconfig-paths": "^3.9.0",
"chalk": "4.1.0",
"enquirer": "~2.3.6",
"fast-glob": "3.2.7",
Expand Down
77 changes: 50 additions & 27 deletions packages/tao/src/shared/nx-plugin.ts
@@ -1,15 +1,16 @@
import { execSync } from 'child_process';
import { sync } from 'fast-glob';
import { existsSync } from 'fs';
import * as path from 'path';
import { register } from '@swc-node/register/register';
import { readDefaultTsConfig } from '@swc-node/register/read-default-tsconfig';

import { appRootPath } from '../utils/app-root';
import { readJsonFile } from '../utils/fileutils';
import { PackageJson } from './package-json';
import { getPackageManagerCommand } from './package-manager';
import { ProjectGraphProcessor } from './project-graph';
import { Workspaces } from './workspace';
import {
ProjectConfiguration,
TargetConfiguration,
WorkspaceJsonConfiguration,
} from './workspace.model';
Expand Down Expand Up @@ -52,7 +53,16 @@ export function loadNxPlugins(
});
} catch (e) {
if (e.code === 'MODULE_NOT_FOUND') {
pluginPath = resolveLocalNxPlugin(moduleName);
const plugin = resolveLocalNxPlugin(moduleName);
const { main } = Object.values(plugin.projectConfig.targets).find(
(x) =>
[
'@nrwl/js:tsc',
'@nrwl/js:swc',
'@nrwl/node:package',
].includes(x.executor)
)?.options;
pluginPath = main ? path.join(appRootPath, main) : plugin.path;
} else {
throw e;
}
Expand Down Expand Up @@ -112,7 +122,7 @@ export function readPluginPackageJson(
const localPluginPath = resolveLocalNxPlugin(pluginName);
if (localPluginPath) {
const localPluginPackageJson = path.join(
localPluginPath,
localPluginPath.path,
'package.json'
);
return {
Expand All @@ -131,41 +141,54 @@ export function readPluginPackageJson(
* @param importPath What is the import path that refers to a potential plugin?
* @returns The path to the built plugin, or null if it doesn't exist
*/
const localPluginCache = {};
const localPluginCache: Record<
string,
{ path: string; projectConfig: ProjectConfiguration }
> = {};
export function resolveLocalNxPlugin(
importPath: string,
root = appRootPath
): string | null {
localPluginCache[importPath] ??= buildLocalPlugin(importPath, root);
): { path: string; projectConfig: ProjectConfiguration } | null {
localPluginCache[importPath] ??= lookupLocalPlugin(importPath, root);
return localPluginCache[importPath];
}

function buildLocalPlugin(importPath: string, root = appRootPath): string {
const workspace = new Workspaces(root).readWorkspaceConfiguration();
let tsNodeAndPathsRegistered = false;
function registerTSTranspiler() {
try {
const tsConfigOptions = readDefaultTsConfig(
path.join(appRootPath, 'tsconfig.base.json')
);
register(tsConfigOptions);

const tsconfigPaths: typeof import('tsconfig-paths') = require('tsconfig-paths');

/**
* Register the custom workspace path mappings with node so that workspace libraries
* can be imported and used within custom workspace lint rules.
*/
return tsconfigPaths.register({
baseUrl: appRootPath,
paths: tsConfigOptions.paths,
});
} catch (err) {}
}

function lookupLocalPlugin(importPath: string, root = appRootPath) {
const workspace = new Workspaces(root).readWorkspaceConfiguration({
ignorePluginInference: true,
});
const plugin = findNxProjectForImportPath(importPath, workspace, root);
if (!plugin) {
return null;
}

const projectConfig = workspace.projects[plugin];
if (!tsNodeAndPathsRegistered) {
registerTSTranspiler();
}

/**
* todo(v14-v15) make this stuff async + use task scheduler
*
* Ideally, we wouldn't invoke nx via execSync here. We should be
* able to run an executor given a project and target programmatically.
* Currently, runExecutor is async and doesn't hit the task orchestrator.
* So to use it, we would have to make a bunch of this stuff async (a breaking change),
* and we would also not benefit from remote or local caches which would be much slower.
* Therefore, currently we use execSync here. We should work towards simplifying
* the task orchestrator API, while consolidating @nrwl/workspace and @nrwl/tao
* to make this something like `await TaskScheduler.runTaskNow({project, target: 'build'})`,
* but that API doesn't exist.
*/
execSync(`${getPackageManagerCommand().exec} nx build ${plugin}`, {
cwd: root,
});
return path.join(root, projectConfig.targets?.build?.options?.outputPath);
const projectConfig = workspace.projects[plugin];
return { path: path.join(root, projectConfig.root), projectConfig };
}

function findNxProjectForImportPath(
Expand Down
21 changes: 13 additions & 8 deletions packages/tao/src/shared/workspace.ts
Expand Up @@ -56,8 +56,9 @@ export class Workspaces {
return wc.defaultProject;
}

readWorkspaceConfiguration(): WorkspaceJsonConfiguration &
NxJsonConfiguration {
readWorkspaceConfiguration(opts?: {
ignorePluginInference?: boolean;
}): WorkspaceJsonConfiguration & NxJsonConfiguration {
const nxJsonPath = path.join(this.root, 'nx.json');
const nxJson = readNxJson(nxJsonPath);
const workspaceFile = workspaceConfigName(this.root);
Expand All @@ -69,7 +70,7 @@ export class Workspaces {
? this.readFromWorkspaceJson()
: buildWorkspaceConfigurationFromGlobs(
nxJson,
globForProjectFiles(this.root, nxJson),
globForProjectFiles(this.root, nxJson, opts?.ignorePluginInference),
(path) => readJsonFile(join(this.root, path))
);

Expand Down Expand Up @@ -430,17 +431,21 @@ export function toProjectName(

let projectGlobCache: string[];
let projectGlobCacheKey: string;
export function globForProjectFiles(root, nxJson?: NxJsonConfiguration) {
export function globForProjectFiles(
root,
nxJson?: NxJsonConfiguration,
ignorePluginInference = false
) {
// Deal w/ Caching
const cacheKey = [root, ...(nxJson?.plugins || [])].join(',');
if (projectGlobCache && cacheKey === projectGlobCacheKey)
return projectGlobCache;
projectGlobCacheKey = cacheKey;

// Load plugins
const plugins = loadNxPlugins(nxJson?.plugins).filter(
(x) => !!x.projectFilePatterns
);
const plugins = ignorePluginInference
? []
: loadNxPlugins(nxJson?.plugins).filter((x) => !!x.projectFilePatterns);
let combinedProjectGlobPattern = `**/+(project.json|package.json${
plugins.length
? '|' + plugins.map((x) => x.projectFilePatterns.join('|')).join('|')
Expand Down Expand Up @@ -543,7 +548,7 @@ export function inferProjectFromNonStandardFile(

export function buildWorkspaceConfigurationFromGlobs(
nxJson: NxJsonConfiguration,
projectFiles: string[] = globForProjectFiles(appRootPath, nxJson), // making this parameter allows devkit to pick up newly created projects
projectFiles: string[], // making this parameter allows devkit to pick up newly created projects
readJson: (string) => any = readJsonFile // making this an arg allows us to reuse in devkit
): WorkspaceJsonConfiguration {
const projects: Record<string, ProjectConfiguration> = {};
Expand Down

0 comments on commit 2dd17ff

Please sign in to comment.