feat(js): add createNodesV2 for typescript plugin (#26788)

<!-- 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
<!-- This is the behavior we have today -->

There is no implementation for the `CreateNodesV2` API.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

There should be an implementation for the `CreateNodesV2` API.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->
<!-- Fixes NXC-790 -->
<!-- Fixes NXC-791 -->

Fixes #
This commit is contained in:
Leosvel Pérez Espinosa 2024-07-10 10:21:58 +02:00 committed by GitHub
parent 2b7b5231fb
commit e31b1689c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 121 additions and 76 deletions

View File

@ -1,8 +1,8 @@
import { type CreateNodesContext } from '@nx/devkit';
import { TempFs } from '@nx/devkit/internal-testing-utils';
import { minimatch } from 'minimatch';
import { TempFs } from 'nx/src/internal-testing-utils/temp-fs';
import { PLUGIN_NAME, TscPluginOptions, createNodes } from './plugin';
import { setupWorkspaceContext } from 'nx/src/utils/workspace-context';
import { PLUGIN_NAME, createNodesV2, type TscPluginOptions } from './plugin';
describe(`Plugin: ${PLUGIN_NAME}`, () => {
let context: CreateNodesContext;
@ -2170,7 +2170,7 @@ async function applyFilesToTempFsAndContext(
await tempFs.createFiles(fileSys);
// @ts-expect-error update otherwise readonly property for testing
context.configFiles = Object.keys(fileSys).filter((file) =>
minimatch(file, createNodes[0], { dot: true })
minimatch(file, createNodesV2[0], { dot: true })
);
setupWorkspaceContext(tempFs.tempDir);
}
@ -2181,8 +2181,11 @@ async function invokeCreateNodesOnMatchingFiles(
) {
const aggregateProjects: Record<string, any> = {};
for (const file of context.configFiles) {
const nodes = await createNodes[1](file, pluginOptions, context);
for (const [projectName, project] of Object.entries(nodes.projects ?? {})) {
const results = await createNodesV2[1]([file], pluginOptions, context);
for (const [, nodes] of results) {
for (const [projectName, project] of Object.entries(
nodes.projects ?? {}
)) {
if (aggregateProjects[projectName]) {
aggregateProjects[projectName].targets = {
...aggregateProjects[projectName].targets,
@ -2193,6 +2196,7 @@ async function invokeCreateNodesOnMatchingFiles(
}
}
}
}
return {
projects: aggregateProjects,
};

View File

@ -1,20 +1,26 @@
import {
createNodesFromFiles,
detectPackageManager,
joinPathFragments,
logger,
normalizePath,
readJsonFile,
writeJsonFile,
type CreateDependencies,
type CreateNodes,
type CreateNodesContext,
type CreateNodesResult,
type CreateNodesV2,
type NxJsonConfiguration,
type ProjectConfiguration,
type TargetConfiguration,
} from '@nx/devkit';
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';
import { minimatch } from 'minimatch';
import { existsSync, readdirSync, statSync } from 'node:fs';
import { basename, dirname, join, relative } from 'node:path';
import { minimatch } from 'minimatch';
import { hashObject } from 'nx/src/hasher/file-hasher';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { getLockFileName } from 'nx/src/plugins/js/lock-file/lock-file';
import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
@ -49,35 +55,68 @@ interface NormalizedPluginOptions {
};
}
const cachePath = join(workspaceDataDirectory, 'tsc.hash');
const targetsCache = readTargetsCache();
type TscProjectResult = Pick<ProjectConfiguration, 'targets'>;
function readTargetsCache(): Record<
string,
Record<string, TargetConfiguration<unknown>>
> {
function readTargetsCache(cachePath: string): Record<string, TscProjectResult> {
return existsSync(cachePath) ? readJsonFile(cachePath) : {};
}
function writeTargetsToCache() {
const oldCache = readTargetsCache();
writeJsonFile(cachePath, {
...oldCache,
...targetsCache,
});
function writeTargetsToCache(
cachePath: string,
results?: Record<string, TscProjectResult>
) {
writeJsonFile(cachePath, results);
}
/**
* @deprecated The 'createDependencies' function is now a no-op. This functionality is included in 'createNodesV2'.
*/
export const createDependencies: CreateDependencies = () => {
writeTargetsToCache();
return [];
};
export const PLUGIN_NAME = '@nx/js/typescript';
const tsConfigGlob = '**/tsconfig*.json';
export const createNodesV2: CreateNodesV2<TscPluginOptions> = [
tsConfigGlob,
async (configFilePaths, options, context) => {
const optionsHash = hashObject(options);
const cachePath = join(workspaceDataDirectory, `tsc-${optionsHash}.hash`);
const targetsCache = readTargetsCache(cachePath);
const normalizedOptions = normalizePluginOptions(options);
try {
return await createNodesFromFiles(
(configFile, options, context) =>
createNodesInternal(configFile, options, context, targetsCache),
configFilePaths,
normalizedOptions,
context
);
} finally {
writeTargetsToCache(cachePath, targetsCache);
}
},
];
export const createNodes: CreateNodes<TscPluginOptions> = [
'**/tsconfig*.json',
tsConfigGlob,
async (configFilePath, options, context) => {
const pluginOptions = normalizePluginOptions(options);
logger.warn(
'`createNodes` is deprecated. Update your plugin to utilize createNodesV2 instead. In Nx 20, this will change to the createNodesV2 API.'
);
const normalizedOptions = normalizePluginOptions(options);
return createNodesInternal(configFilePath, normalizedOptions, context, {});
},
];
async function createNodesInternal(
configFilePath: string,
options: NormalizedPluginOptions,
context: CreateNodesContext,
targetsCache: Record<string, TscProjectResult>
): Promise<CreateNodesResult> {
const projectRoot = dirname(configFilePath);
const fullConfigPath = joinPathFragments(
context.workspaceRoot,
@ -103,7 +142,7 @@ export const createNodes: CreateNodes<TscPluginOptions> = [
const nodeHash = await calculateHashForCreateNodes(
projectRoot,
pluginOptions,
options,
context,
[getLockFileName(detectPackageManager(context.workspaceRoot))]
);
@ -113,20 +152,21 @@ export const createNodes: CreateNodes<TscPluginOptions> = [
targetsCache[cacheKey] ??= buildTscTargets(
fullConfigPath,
projectRoot,
pluginOptions,
options,
context
);
const { targets } = targetsCache[cacheKey];
return {
projects: {
[projectRoot]: {
projectType: 'library',
targets: targetsCache[cacheKey],
targets,
},
},
};
},
];
}
function buildTscTargets(
configFilePath: string,
@ -220,7 +260,7 @@ function buildTscTargets(
};
}
return targets;
return { targets };
}
function getInputs(

View File

@ -1,4 +1,5 @@
export {
createDependencies,
createNodes,
createNodesV2,
} from './src/plugins/typescript/plugin';