diff --git a/projects/nx-verdaccio/src/executors/env-bootstrap/bootstrap-env.ts b/projects/nx-verdaccio/src/executors/env-bootstrap/bootstrap-env.ts index f5d85cc7..aec3e601 100644 --- a/projects/nx-verdaccio/src/executors/env-bootstrap/bootstrap-env.ts +++ b/projects/nx-verdaccio/src/executors/env-bootstrap/bootstrap-env.ts @@ -1,5 +1,6 @@ import { join } from 'node:path'; import { + type RegistryResult, startVerdaccioServer, type StartVerdaccioOptions, type VercaddioServerResult, @@ -28,7 +29,7 @@ export async function bootstrapEnvironment( const { verbose, environmentRoot, storage, ...rest } = options; const parsedStorage = storage ?? join(environmentRoot, 'storage'); - let registryResult; + let registryResult: RegistryResult try { registryResult = await startVerdaccioServer({ storage: parsedStorage, diff --git a/projects/nx-verdaccio/src/plugin/caching.ts b/projects/nx-verdaccio/src/plugin/caching.ts index 45271fe9..bef0bb18 100644 --- a/projects/nx-verdaccio/src/plugin/caching.ts +++ b/projects/nx-verdaccio/src/plugin/caching.ts @@ -1,6 +1,6 @@ import { hashObject } from 'nx/src/hasher/file-hasher'; import { - type CreateNodesResult, + type ProjectConfiguration, readJsonFile, writeJsonFile, } from '@nx/devkit'; @@ -36,7 +36,7 @@ export function setCacheRecord( export function readTargetsCache( cachePath: string -): Record { +): Record> { return process.env.NX_CACHE_PROJECT_GRAPH !== 'false' && existsSync(cachePath) ? readJsonFile(cachePath) : {}; @@ -44,7 +44,7 @@ export function readTargetsCache( export function writeTargetsToCache( cachePath: string, - results: Record + results: Record> ) { writeJsonFile(cachePath, results); } diff --git a/projects/nx-verdaccio/src/plugin/nx-verdaccio.plugin.ts b/projects/nx-verdaccio/src/plugin/nx-verdaccio.plugin.ts index 9a4ec127..b904e620 100644 --- a/projects/nx-verdaccio/src/plugin/nx-verdaccio.plugin.ts +++ b/projects/nx-verdaccio/src/plugin/nx-verdaccio.plugin.ts @@ -6,8 +6,8 @@ import { logger, type ProjectConfiguration, readJsonFile, - type TargetConfiguration, } from '@nx/devkit'; +import { readFile } from 'node:fs/promises'; import { dirname, join } from 'node:path'; import type { BuildEnvPluginCreateNodeOptions } from './schema'; import { @@ -23,7 +23,7 @@ import { setCacheRecord, writeTargetsToCache, } from './caching'; -import { createTargets } from './targets/create-targets'; +import { createProjectConfiguration } from './targets/create-targets'; const PROJECT_JSON_FILE_GLOB = '**/project.json'; @@ -38,15 +38,11 @@ export const createNodesV2: CreateNodesV2 = [ const targetsCache = readTargetsCache(nxVerdaccioEnvPluginCachePath); try { return await createNodesFromFiles( - (projectConfigurationFile, internalOptions) => { - const projectConfiguration: ProjectConfiguration = readJsonFile( - join(process.cwd(), projectConfigurationFile) - ); - const projectRoot = dirname(projectConfigurationFile); - const hashData = { - projectRoot, - internalOptions, - }; + async (projectConfigurationFile, internalOptions) => { + const projectConfiguration: ProjectConfiguration = await readFile( + join(process.cwd(), projectConfigurationFile), + 'utf8' + ).then(JSON.parse); if ( !('name' in projectConfiguration) || typeof projectConfiguration.name !== 'string' @@ -55,28 +51,35 @@ export const createNodesV2: CreateNodesV2 = [ } const normalizedOptions = normalizeCreateNodesOptions(options); - - let cachedProjectTargets = getCacheRecord< - Record + const projectRoot = dirname(projectConfigurationFile); + const hashData = { + projectRoot, + internalOptions, + }; + let cachedProjectConfiguration = getCacheRecord< + Partial >(targetsCache, projectRoot, hashData); - if (cachedProjectTargets === undefined) { - cachedProjectTargets = createTargets( + if (cachedProjectConfiguration === undefined) { + cachedProjectConfiguration = createProjectConfiguration( projectConfiguration, normalizedOptions ); - setCacheRecord( - targetsCache, - projectRoot, - hashData, - cachedProjectTargets - ); + if (cachedProjectConfiguration.targets) { + setCacheRecord( + targetsCache, + projectRoot, + hashData, + cachedProjectConfiguration + ); + } } - + const { targets, namedInputs = {} } = cachedProjectConfiguration; return { projects: { [projectRoot]: { - targets: cachedProjectTargets, + namedInputs, + targets, }, }, }; @@ -96,6 +99,7 @@ export const createNodes: CreateNodes = [ ( projectConfigurationFile: string, options: NormalizedCreateNodeOptions, + // eslint-disable-next-line @typescript-eslint/no-unused-vars context: CreateNodesContext ) => { logger.warn( @@ -108,7 +112,8 @@ export const createNodes: CreateNodes = [ return { projects: { [projectRoot]: { - targets: createTargets(projectConfiguration, options), + targets: createProjectConfiguration(projectConfiguration, options) + .targets, }, }, }; diff --git a/projects/nx-verdaccio/src/plugin/targets/create-targets.ts b/projects/nx-verdaccio/src/plugin/targets/create-targets.ts index 52e7a6d2..f3ac1653 100644 --- a/projects/nx-verdaccio/src/plugin/targets/create-targets.ts +++ b/projects/nx-verdaccio/src/plugin/targets/create-targets.ts @@ -1,5 +1,5 @@ import type { BuildEnvPluginCreateNodeOptions } from '../schema'; -import type { CreateNodesResult, ProjectConfiguration } from '@nx/devkit'; +import type { ProjectConfiguration } from '@nx/devkit'; import { normalizeCreateNodesOptions } from '../normalize-create-nodes-options'; import { getEnvTargets, @@ -9,10 +9,11 @@ import { } from './environment.targets'; import { getPkgTargets, isPkgProject } from './package.targets'; -export function createTargets( +export function createProjectConfiguration( projectConfiguration: ProjectConfiguration, options: BuildEnvPluginCreateNodeOptions -): CreateNodesResult['projects'][string]['targets'] { +): Pick & + Partial> { const { environments, packages } = normalizeCreateNodesOptions(options); if ( @@ -22,20 +23,31 @@ export function createTargets( return {}; } + // unfortunately namedInputs are not picked up by tasks graph: Error: Input 'build-artifacts' is not defined + const namedInputs: ProjectConfiguration['namedInputs'] = { + 'build-artifacts': [ + '{projectRoot}/**/*.{js,ts,tsx}', + '!{projectRoot}/**/*.spec.{ts,tsx}', + ], + }; return { - // === ENVIRONMENT TARGETS === ...(isEnvProject(projectConfiguration, environments) && { - // start-verdaccio, stop-verdaccio - ...verdaccioTargets(projectConfiguration, { - environmentsDir: environments.environmentsDir, - }), - // env-bootstrap-env, env-setup-env, install-env (intermediate target to run dependency targets) - ...getEnvTargets(projectConfiguration, environments), - // adjust targets to run env-setup-env - ...updateEnvTargetNames(projectConfiguration, environments), + namedInputs, + // === ENVIRONMENT TARGETS === + targets: { + // start-verdaccio, stop-verdaccio + ...verdaccioTargets(projectConfiguration, { + environmentsDir: environments.environmentsDir, + }), + // env-bootstrap-env, env-setup-env, install-env (intermediate target to run dependency targets) + ...getEnvTargets(projectConfiguration, environments), + // adjust targets to run env-setup-env + ...updateEnvTargetNames(projectConfiguration, environments), + }, + }), + ...(isPkgProject(projectConfiguration, packages) && { + // === PACKAGE TARGETS === + targets: getPkgTargets(), }), - // === PACKAGE TARGETS === - // pkg-publish, pkg-install - ...(isPkgProject(projectConfiguration, packages) && getPkgTargets()), }; } diff --git a/projects/nx-verdaccio/src/plugin/targets/environment.targets.ts b/projects/nx-verdaccio/src/plugin/targets/environment.targets.ts index 3b7c8c96..17f4d1f3 100644 --- a/projects/nx-verdaccio/src/plugin/targets/environment.targets.ts +++ b/projects/nx-verdaccio/src/plugin/targets/environment.targets.ts @@ -12,7 +12,6 @@ import { import { PACKAGE_NAME } from '../constants'; import { EXECUTOR_ENVIRONMENT_KILL_PROCESS } from '../../executors/kill-process/constant'; import { EXECUTOR_ENVIRONMENT_SETUP } from '../../executors/env-setup/constants'; -import * as constants from 'constants'; import { iterateEntries } from '../../internal/transform'; export const TARGET_ENVIRONMENT_VERDACCIO_START = 'pb-ve-env-verdaccio-start'; @@ -21,6 +20,8 @@ export const TARGET_ENVIRONMENT_INSTALL = 'pb-ve-env-install'; export const TARGET_ENVIRONMENT_SETUP = 'pb-ve-env-setup'; export const TARGET_ENVIRONMENT_VERDACCIO_STOP = 'pb-ve-env-verdaccio-stop'; +const VERDACCIO_STORAGE_DIR = 'storage'; + export function isEnvProject( projectConfig: ProjectConfiguration, options: NormalizedCreateNodeOptions['environments'] @@ -63,7 +64,8 @@ export function verdaccioTargets( const { name: envProject } = projectConfig; const { environmentsDir, ...verdaccioOptions } = options; const environmentDir = join(environmentsDir, envProject); - + // unless the verdaccio server port and pid is always the same, i don't see how to enable Distributed Task Execution with Nx agents... + // we cannot control which agent will run the task and we must enable cache for the task to be distributed return { [TARGET_ENVIRONMENT_VERDACCIO_START]: { // @TODO: consider using the executor function directly to reduce the number of targets @@ -72,7 +74,7 @@ export function verdaccioTargets( options: { config: '.verdaccio/config.yml', port: uniquePort(), - storage: join(environmentDir, 'storage'), + storage: join(environmentDir, VERDACCIO_STORAGE_DIR), clear: true, environmentDir, projectName: envProject, @@ -89,6 +91,14 @@ export function verdaccioTargets( }; } +/** + * Create new targets wrapping env-bootstrap, env-setup executors + * install-env (intermediate target to run dependency targets) + * + * @param projectConfig + * @param options + * @returns + */ export function getEnvTargets( projectConfig: ProjectConfiguration, options: NormalizedCreateNodeOptions['environments'] @@ -98,6 +108,8 @@ export function getEnvTargets( const environmentRoot = join(environmentsDir, envProject); return { [TARGET_ENVIRONMENT_BOOTSTRAP]: { + //? how to cache this target individually? since it starts the verdaccio server + // problem is: if we can't cache, we can't distribute the task executor: `${PACKAGE_NAME}:${EXECUTOR_ENVIRONMENT_BOOTSTRAP}`, options: { environmentRoot }, }, @@ -114,13 +126,31 @@ export function getEnvTargets( }, // runs env-bootstrap-env, install-env and stop-verdaccio [TARGET_ENVIRONMENT_SETUP]: { + // list of inputs that all subsequent tasks depend on + inputs: [ + '{projectRoot}/project.json', + { + runtime: 'node --version', + }, + { + runtime: 'npm --version', + }, + { + externalDependencies: ['verdaccio'], + }, + // depends on underlying project being e2e tested and its own dependencies + // ! it's important that implicitDependencies are correctly configured between this project and the project being tested + // '^build-artifacts', + '^production', + ], outputs: [ - '{options.environmentRoot}/node_modules', - '{options.environmentRoot}/package.json', + `{options.environmentRoot}/${VERDACCIO_STORAGE_DIR}`, '{options.environmentRoot}/.npmrc', + '{options.environmentRoot}/package.json', '{options.environmentRoot}/package-lock.json', + '{options.environmentRoot}/node_modules', ], - cache: false, // # @TODO enable by default after more research on cache size is done + cache: true, executor: `${PACKAGE_NAME}:${EXECUTOR_ENVIRONMENT_SETUP}`, options: { environmentRoot, @@ -129,6 +159,13 @@ export function getEnvTargets( }; } +/** + * adjust targets to run env-setup-env + * this will add the dependsOn property to the targets that are in the targetNames array (usually e2e) + * @param projectConfig + * @param options + * @returns + */ export function updateEnvTargetNames( projectConfig: ProjectConfiguration, options: Required>