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:
parent
2b7b5231fb
commit
e31b1689c4
@ -1,8 +1,8 @@
|
|||||||
import { type CreateNodesContext } from '@nx/devkit';
|
import { type CreateNodesContext } from '@nx/devkit';
|
||||||
|
import { TempFs } from '@nx/devkit/internal-testing-utils';
|
||||||
import { minimatch } from 'minimatch';
|
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 { setupWorkspaceContext } from 'nx/src/utils/workspace-context';
|
||||||
|
import { PLUGIN_NAME, createNodesV2, type TscPluginOptions } from './plugin';
|
||||||
|
|
||||||
describe(`Plugin: ${PLUGIN_NAME}`, () => {
|
describe(`Plugin: ${PLUGIN_NAME}`, () => {
|
||||||
let context: CreateNodesContext;
|
let context: CreateNodesContext;
|
||||||
@ -2170,7 +2170,7 @@ async function applyFilesToTempFsAndContext(
|
|||||||
await tempFs.createFiles(fileSys);
|
await tempFs.createFiles(fileSys);
|
||||||
// @ts-expect-error update otherwise readonly property for testing
|
// @ts-expect-error update otherwise readonly property for testing
|
||||||
context.configFiles = Object.keys(fileSys).filter((file) =>
|
context.configFiles = Object.keys(fileSys).filter((file) =>
|
||||||
minimatch(file, createNodes[0], { dot: true })
|
minimatch(file, createNodesV2[0], { dot: true })
|
||||||
);
|
);
|
||||||
setupWorkspaceContext(tempFs.tempDir);
|
setupWorkspaceContext(tempFs.tempDir);
|
||||||
}
|
}
|
||||||
@ -2181,8 +2181,11 @@ async function invokeCreateNodesOnMatchingFiles(
|
|||||||
) {
|
) {
|
||||||
const aggregateProjects: Record<string, any> = {};
|
const aggregateProjects: Record<string, any> = {};
|
||||||
for (const file of context.configFiles) {
|
for (const file of context.configFiles) {
|
||||||
const nodes = await createNodes[1](file, pluginOptions, context);
|
const results = await createNodesV2[1]([file], pluginOptions, context);
|
||||||
for (const [projectName, project] of Object.entries(nodes.projects ?? {})) {
|
for (const [, nodes] of results) {
|
||||||
|
for (const [projectName, project] of Object.entries(
|
||||||
|
nodes.projects ?? {}
|
||||||
|
)) {
|
||||||
if (aggregateProjects[projectName]) {
|
if (aggregateProjects[projectName]) {
|
||||||
aggregateProjects[projectName].targets = {
|
aggregateProjects[projectName].targets = {
|
||||||
...aggregateProjects[projectName].targets,
|
...aggregateProjects[projectName].targets,
|
||||||
@ -2193,6 +2196,7 @@ async function invokeCreateNodesOnMatchingFiles(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
projects: aggregateProjects,
|
projects: aggregateProjects,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,20 +1,26 @@
|
|||||||
import {
|
import {
|
||||||
|
createNodesFromFiles,
|
||||||
detectPackageManager,
|
detectPackageManager,
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
|
logger,
|
||||||
normalizePath,
|
normalizePath,
|
||||||
readJsonFile,
|
readJsonFile,
|
||||||
writeJsonFile,
|
writeJsonFile,
|
||||||
type CreateDependencies,
|
type CreateDependencies,
|
||||||
type CreateNodes,
|
type CreateNodes,
|
||||||
type CreateNodesContext,
|
type CreateNodesContext,
|
||||||
|
type CreateNodesResult,
|
||||||
|
type CreateNodesV2,
|
||||||
type NxJsonConfiguration,
|
type NxJsonConfiguration,
|
||||||
|
type ProjectConfiguration,
|
||||||
type TargetConfiguration,
|
type TargetConfiguration,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
|
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
|
||||||
import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';
|
import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';
|
||||||
|
import { minimatch } from 'minimatch';
|
||||||
import { existsSync, readdirSync, statSync } from 'node:fs';
|
import { existsSync, readdirSync, statSync } from 'node:fs';
|
||||||
import { basename, dirname, join, relative } from 'node:path';
|
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
|
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||||
import { getLockFileName } from 'nx/src/plugins/js/lock-file/lock-file';
|
import { getLockFileName } from 'nx/src/plugins/js/lock-file/lock-file';
|
||||||
import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
|
import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
|
||||||
@ -49,35 +55,68 @@ interface NormalizedPluginOptions {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const cachePath = join(workspaceDataDirectory, 'tsc.hash');
|
type TscProjectResult = Pick<ProjectConfiguration, 'targets'>;
|
||||||
const targetsCache = readTargetsCache();
|
|
||||||
|
|
||||||
function readTargetsCache(): Record<
|
function readTargetsCache(cachePath: string): Record<string, TscProjectResult> {
|
||||||
string,
|
|
||||||
Record<string, TargetConfiguration<unknown>>
|
|
||||||
> {
|
|
||||||
return existsSync(cachePath) ? readJsonFile(cachePath) : {};
|
return existsSync(cachePath) ? readJsonFile(cachePath) : {};
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeTargetsToCache() {
|
function writeTargetsToCache(
|
||||||
const oldCache = readTargetsCache();
|
cachePath: string,
|
||||||
writeJsonFile(cachePath, {
|
results?: Record<string, TscProjectResult>
|
||||||
...oldCache,
|
) {
|
||||||
...targetsCache,
|
writeJsonFile(cachePath, results);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated The 'createDependencies' function is now a no-op. This functionality is included in 'createNodesV2'.
|
||||||
|
*/
|
||||||
export const createDependencies: CreateDependencies = () => {
|
export const createDependencies: CreateDependencies = () => {
|
||||||
writeTargetsToCache();
|
|
||||||
return [];
|
return [];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PLUGIN_NAME = '@nx/js/typescript';
|
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> = [
|
export const createNodes: CreateNodes<TscPluginOptions> = [
|
||||||
'**/tsconfig*.json',
|
tsConfigGlob,
|
||||||
async (configFilePath, options, context) => {
|
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 projectRoot = dirname(configFilePath);
|
||||||
const fullConfigPath = joinPathFragments(
|
const fullConfigPath = joinPathFragments(
|
||||||
context.workspaceRoot,
|
context.workspaceRoot,
|
||||||
@ -103,7 +142,7 @@ export const createNodes: CreateNodes<TscPluginOptions> = [
|
|||||||
|
|
||||||
const nodeHash = await calculateHashForCreateNodes(
|
const nodeHash = await calculateHashForCreateNodes(
|
||||||
projectRoot,
|
projectRoot,
|
||||||
pluginOptions,
|
options,
|
||||||
context,
|
context,
|
||||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||||
);
|
);
|
||||||
@ -113,20 +152,21 @@ export const createNodes: CreateNodes<TscPluginOptions> = [
|
|||||||
targetsCache[cacheKey] ??= buildTscTargets(
|
targetsCache[cacheKey] ??= buildTscTargets(
|
||||||
fullConfigPath,
|
fullConfigPath,
|
||||||
projectRoot,
|
projectRoot,
|
||||||
pluginOptions,
|
options,
|
||||||
context
|
context
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { targets } = targetsCache[cacheKey];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
projects: {
|
projects: {
|
||||||
[projectRoot]: {
|
[projectRoot]: {
|
||||||
projectType: 'library',
|
projectType: 'library',
|
||||||
targets: targetsCache[cacheKey],
|
targets,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
];
|
|
||||||
|
|
||||||
function buildTscTargets(
|
function buildTscTargets(
|
||||||
configFilePath: string,
|
configFilePath: string,
|
||||||
@ -220,7 +260,7 @@ function buildTscTargets(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return targets;
|
return { targets };
|
||||||
}
|
}
|
||||||
|
|
||||||
function getInputs(
|
function getInputs(
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
export {
|
export {
|
||||||
createDependencies,
|
createDependencies,
|
||||||
createNodes,
|
createNodes,
|
||||||
|
createNodesV2,
|
||||||
} from './src/plugins/typescript/plugin';
|
} from './src/plugins/typescript/plugin';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user